Skip to content

Commit 9faa0bc

Browse files
mikekistlercaptainsafiajamesmontemagno
authored
Add release notes for OpenAPI operation transformers (#9868)
* Add release notes for OpenAPI operation transformers * Apply suggestions from PR review Co-authored-by: Safia Abdalla <[email protected]> * fix linter * sub headers --------- Co-authored-by: Safia Abdalla <[email protected]> Co-authored-by: James Montemagno <[email protected]>
1 parent d5f1720 commit 9faa0bc

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

release-notes/10.0/preview/preview3/aspnetcore.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Here's a summary of what's new in ASP.NET Core in this preview release:
1010
- [Validation support in minimal APIs](#validation-support-in-minimal-apis)
1111
- [OpenAPI support enabled by default in the ASP.NET Core Web API (native AOT) template](#openapi-support-enabled-by-default-in-the-aspnet-core-web-api-native-aot-template)
1212
- [Support for Server-Sent Events (SSE)](#support-for-server-sent-events-sse)
13+
- [OpenAPI operation transformers](#openapi-operation-transformers)
1314

1415
ASP.NET Core updates in .NET 10:
1516

@@ -260,6 +261,131 @@ app.MapGet("/json-item", (CancellationToken cancellationToken) =>
260261
});
261262
```
262263

264+
## OpenAPI operation transformers
265+
266+
The new `AddOpenApiOperationTransformer` API makes it easier to customize OpenAPI documentation for your ASP.NET Core endpoints. This API allows you to register custom operation transformers, which modify OpenAPI operation definitions programmatically.
267+
This feature reduces the need for manual intervention or external tools, streamlining the API documentation process.
268+
269+
### Key Features
270+
271+
- **Targeted Transformations**: Use custom or predefined logic to modify individual OpenAPI operations.
272+
- **Support for Multiple Transformers**: Chain multiple transformers to apply different transformations sequentially.
273+
274+
#### Example: Custom transformer
275+
276+
Here’s how you can use the `AddOpenApiOperationTransformer` extension method with a custom transformer:
277+
278+
```csharp
279+
var builder = WebApplication.CreateBuilder(args);
280+
var app = builder.Build();
281+
282+
app.MapGet("/", () => "Hello World!")
283+
.AddOpenApiOperationTransformer((operation, context, cancellationToken) =>
284+
{
285+
operation.Description = "This endpoint returns a greeting message.";
286+
return Task.CompletedTask;
287+
});
288+
289+
app.Run();
290+
```
291+
292+
#### Example: Predefined and chained transformers
293+
294+
You can also create predefined transformers that you can use on multiple endpoints. These are defined as extension methods on `RouteHandlerBuilder`, and return a `RouteHandlerBuilder` so they can be chained with other methods like `WithName`, `WithTags`, and other operation transformers.
295+
Some example use cases are a transformer to add a description for a specific response code, or a transformer to add a response header.
296+
297+
```csharp
298+
public static class ExtensionMethods
299+
{
300+
public static RouteHandlerBuilder WithResponseDescription(this RouteHandlerBuilder builder, int statusCode, string description)
301+
{
302+
builder.AddOpenApiOperationTransformer((operation, context, cancellationToken) =>
303+
{
304+
var response = operation.Responses?.TryGetValue(statusCode.ToString(), out var r) == true ? r : null;
305+
// The following line uses the new "null conditional assignment" feature of C# 14
306+
response?.Description = description;
307+
return Task.CompletedTask;
308+
});
309+
return builder;
310+
}
311+
312+
public static RouteHandlerBuilder WithLocationHeader(this RouteHandlerBuilder builder)
313+
{
314+
builder.AddOpenApiOperationTransformer((operation, context, cancellationToken) =>
315+
{
316+
var createdResponse = operation?.Responses?.TryGetValue("201", out var r) == true ? r : null;
317+
// The following line uses the new "null conditional assignment" feature of C# 14
318+
createdResponse?.Headers["Location"] = new OpenApiHeader
319+
{
320+
Description = "Location of the created resource.",
321+
Required = true,
322+
Schema = new OpenApiSchema
323+
{
324+
Type = JsonSchemaType.String,
325+
Format = "uri"
326+
}
327+
};
328+
return Task.CompletedTask;
329+
});
330+
return builder;
331+
}
332+
}
333+
```
334+
335+
Here's how you can use the above transformers in your application:
336+
337+
```csharp
338+
app.MapPost("/todos", (Todo todo) =>
339+
TypedResults.Created($"/todos/{todo.Id}", todo))
340+
.WithName("CreateTodo")
341+
.WithResponseDescription(201, "The todo was created successfully.")
342+
.WithLocationHeader();
343+
```
344+
345+
and the resulting OpenAPI document will look like this:
346+
347+
<!-- In the docs, highlight the response description and response header -->
348+
```json
349+
"paths": {
350+
"/todos": {
351+
"post": {
352+
"operationId": "CreateTodo",
353+
"requestBody": {
354+
"content": {
355+
"application/json": {
356+
"schema": {
357+
"$ref": "#/components/schemas/Todo"
358+
}
359+
}
360+
},
361+
"required": true
362+
},
363+
"responses": {
364+
"201": {
365+
"description": "The todo was created successfully.",
366+
"headers": {
367+
"Location": {
368+
"description": "Location of the created resource.",
369+
"required": true,
370+
"schema": {
371+
"type": "string",
372+
"format": "uri"
373+
}
374+
}
375+
},
376+
"content": {
377+
"application/json": {
378+
"schema": {
379+
"$ref": "#/components/schemas/Todo"
380+
}
381+
}
382+
}
383+
}
384+
}
385+
}
386+
}
387+
```
388+
263389
## Community contributors
264390

265391
Thank you contributors! ❤️

0 commit comments

Comments
 (0)