|
| 1 | +:::moniker range="= aspnetcore-9.0" |
| 2 | + |
| 3 | +<a name="transformers"></a> |
| 4 | + |
| 5 | +## OpenAPI document transformers |
| 6 | + |
| 7 | +Transformers provide an API for modifying the OpenAPI document with user-defined customizations. Transformers are useful for scenarios like: |
| 8 | + |
| 9 | +* Adding parameters to all operations in a document. |
| 10 | +* Modifying descriptions for parameters or operations. |
| 11 | +* Adding top-level information to the OpenAPI document. |
| 12 | + |
| 13 | +Transformers fall into three categories: |
| 14 | + |
| 15 | +* Document transformers have access to the entire OpenAPI document. These can be used to make global modifications to the document. |
| 16 | +* Operation transformers apply to each individual operation. Each individual operation is a combination of path and HTTP method. These can be used to modify parameters or responses on endpoints. |
| 17 | +* Schema transformers apply to each schema in the document. These can be used to modify the schema of request or response bodies, or any nested schemas. |
| 18 | + |
| 19 | +Transformers can be registered onto the document by calling the <xref:Microsoft.AspNetCore.OpenApi.OpenApiOptions.AddDocumentTransformer%2A> method on the <xref:Microsoft.AspNetCore.OpenApi.OpenApiOptions> object. The following snippet shows different ways to register transformers onto the document: |
| 20 | + |
| 21 | +* Register a document transformer using a delegate. |
| 22 | +* Register a document transformer using an instance of <xref:Microsoft.AspNetCore.OpenApi.IOpenApiDocumentTransformer>. |
| 23 | +* Register a document transformer using a DI-activated <xref:Microsoft.AspNetCore.OpenApi.IOpenApiDocumentTransformer>. |
| 24 | +* Register an operation transformer using a delegate. |
| 25 | +* Register an operation transformer using an instance of <xref:Microsoft.AspNetCore.OpenApi.IOpenApiOperationTransformer>. |
| 26 | +* Register an operation transformer using a DI-activated <xref:Microsoft.AspNetCore.OpenApi.IOpenApiOperationTransformer>. |
| 27 | +* Register a schema transformer using a delegate. |
| 28 | +* Register a schema transformer using an instance of <xref:Microsoft.AspNetCore.OpenApi.IOpenApiSchemaTransformer>. |
| 29 | +* Register a schema transformer using a DI-activated <xref:Microsoft.AspNetCore.OpenApi.IOpenApiSchemaTransformer>. |
| 30 | + |
| 31 | +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_transUse&highlight=8-19)] |
| 32 | + |
| 33 | +### Execution order for transformers |
| 34 | + |
| 35 | +Transformers are executed as follows: |
| 36 | + |
| 37 | +* Schema transformers are executed when a schema is registered to the document. Schema transformers are executed in the order in which they were added. |
| 38 | +All schemas are added to the document before any operation processing occurs, so all schema transformers are executed before any operation transformers. |
| 39 | +* Operation transformers are executed when an operation is added to the document. Operation transformers are executed in the order in which they were added. |
| 40 | +All operations are added to the document before any document transformers are executed. |
| 41 | +* Document transformers are executed when the document is generated. This is the final pass over the document, and all operations and schemas have been add by this point. |
| 42 | +* When an app is configured to generate multiple OpenAPI documents, transformers are executed for each document independently. |
| 43 | + |
| 44 | +For example, in the following snippet: |
| 45 | +* `SchemaTransformer2` is executed and has access to the modifications made by `SchemaTransformer1`. |
| 46 | +* Both `OperationTransformer1` and `OperationTransformer2` have access to the modifications made by both schema transformers for the types involved in the operation they are called to process. |
| 47 | +* `OperationTransformer2` is executed after `OperationTransformer1`, so it has access to the modifications made by `OperationTransformer1`. |
| 48 | +* Both `DocumentTransformer1` and `DocumentTransformer2` are executed after all operations and schemas have been added to the document, so they have access to all modifications made by the operation and schema transformers. |
| 49 | +* `DocumentTransformer2` is executed after `DocumentTransformer1`, so it has access to the modifications made by `DocumentTransformer1`. |
| 50 | + |
| 51 | +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_transInOut&highlight=6-14)] |
| 52 | + |
| 53 | +## Use document transformers |
| 54 | + |
| 55 | +Document transformers have access to a context object that includes: |
| 56 | + |
| 57 | +* The name of the document being modified. |
| 58 | +* The <xref:Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionGroupCollectionProvider.ApiDescriptionGroups> associated with that document. |
| 59 | +* The <xref:System.IServiceProvider> used in document generation. |
| 60 | + |
| 61 | +Document transformers can also mutate the OpenAPI document that is generated. The following example demonstrates a document transformer that adds some information about the API to the OpenAPI document. |
| 62 | + |
| 63 | +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_documenttransformer1)] |
| 64 | + |
| 65 | +Service-activated document transformers can utilize instances from DI to modify the app. The following sample demonstrates a document transformer that uses the <xref:Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider> service from the authentication layer. It checks if any JWT bearer-related schemes are registered in the app and adds them to the OpenAPI document's top level: |
| 66 | + |
| 67 | +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_documenttransformer2)] |
| 68 | + |
| 69 | +Document transformers are unique to the document instance they're associated with. In the following example, a transformer: |
| 70 | + |
| 71 | +* Registers authentication-related requirements to the `internal` document. |
| 72 | +* Leaves the `public` document unmodified. |
| 73 | + |
| 74 | +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_multidoc_operationtransformer1)] |
| 75 | + |
| 76 | +## Use operation transformers |
| 77 | + |
| 78 | +Operations are unique combinations of HTTP paths and methods in an OpenAPI document. Operation transformers are helpful when a modification: |
| 79 | + |
| 80 | +* Should be made to each endpoint in an app, or |
| 81 | +* Conditionally applied to certain routes. |
| 82 | + |
| 83 | +Operation transformers have access to a context object which contains: |
| 84 | + |
| 85 | +* The name of the document the operation belongs to. |
| 86 | +* The <xref:Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription> associated with the operation. |
| 87 | +* The <xref:System.IServiceProvider> used in document generation. |
| 88 | + |
| 89 | +For example, the following operation transformer adds `500` as a response status code supported by all operations in the document. |
| 90 | + |
| 91 | +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_operationtransformer1)] |
| 92 | + |
| 93 | +## Use schema transformers |
| 94 | + |
| 95 | +Schemas are the data models that are used in request and response bodies in an OpenAPI document. Schema transformers are useful when a modification: |
| 96 | + |
| 97 | +* Should be made to each schema in the document, or |
| 98 | +* Conditionally applied to certain schemas. |
| 99 | + |
| 100 | +Schema transformers have access to a context object which contains: |
| 101 | + |
| 102 | +* The name of the document the schema belongs to. |
| 103 | +* The JSON type information associated with the target schema. |
| 104 | +* The <xref:System.IServiceProvider> used in document generation. |
| 105 | + |
| 106 | +For example, the following schema transformer sets the `format` of decimal types to `decimal` instead of `double`: |
| 107 | + |
| 108 | +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_schematransformer1)] |
| 109 | + |
| 110 | +## Customize schema reuse |
| 111 | + |
| 112 | +After all transformers have been applied, the framework makes a pass over the document to transfer certain schemas |
| 113 | +to the `components.schemas` section, replacing them with `$ref` references to the transferred schema. |
| 114 | +This reduces the size of the document and makes it easier to read. |
| 115 | + |
| 116 | +The details of this processing are complicated and might change in future versions of .NET, but in general: |
| 117 | + |
| 118 | +* Schemas for class/record/struct types are replaced with a `$ref` to a schema in `components.schemas` |
| 119 | + if they appear more than once in the document. |
| 120 | +* Schemas for primitive types and standard collections are left inline. |
| 121 | +* Schemas for enum types are always replaced with a `$ref` to a schema in components.schemas. |
| 122 | + |
| 123 | +Typically the name of the schema in `components.schemas` is the name of the class/record/struct type, |
| 124 | +but in some circumstances a different name must be used. |
| 125 | + |
| 126 | +ASP.NET Core lets you customize which schemas are replaced with a `$ref` to a schema in `components.schemas` |
| 127 | +using the <xref:Microsoft.AspNetCore.OpenApi.OpenApiOptions.CreateSchemaReferenceId> property of <xref:Microsoft.AspNetCore.OpenApi.OpenApiOptions>. |
| 128 | +This property is a delegate that takes a <xref:System.Text.Json.Serialization.Metadata.JsonTypeInfo> object and returns the name of the schema |
| 129 | +in `components.schemas` that should be used for that type. |
| 130 | +The framework provides a default implementation of this delegate, <xref:Microsoft.AspNetCore.OpenApi.OpenApiOptions.CreateDefaultSchemaReferenceId%2A> |
| 131 | +that uses the name of the type, but you can replace it with your own implementation. |
| 132 | + |
| 133 | +As a simple example of this customization, you might choose to always inline enum schemas. |
| 134 | +This is done by setting <xref:Microsoft.AspNetCore.OpenApi.OpenApiOptions.CreateSchemaReferenceId> to a delegate |
| 135 | +that returns null for enum types, and otherwise returns the value from the default implementation. |
| 136 | +The following code shows how to do this: |
| 137 | + |
| 138 | +```csharp |
| 139 | +builder.Services.AddOpenApi(options => |
| 140 | +{ |
| 141 | + // Always inline enum schemas |
| 142 | + options.CreateSchemaReferenceId = (type) => |
| 143 | + type.Type.IsEnum ? null : OpenApiOptions.CreateDefaultSchemaReferenceId(type); |
| 144 | +}); |
| 145 | +``` |
| 146 | + |
| 147 | +## Additional resources |
| 148 | + |
| 149 | +* <xref:fundamentals/openapi/using-openapi-documents> |
| 150 | +* [OpenAPI specification](https://spec.openapis.org/oas/v3.0.3) |
| 151 | + |
| 152 | +:::moniker-end |
0 commit comments