diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index e7984d6c6332..fe2520579040 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -1,4 +1,4 @@ -{ +{ "redirections": [ { "source_path": "aspnetcore/web-api/microsoft.dotnet-openapi.md", @@ -10,6 +10,11 @@ "redirect_url": "/aspnet/core/fundamentals/openapi/aspnetcore-openapi", "redirect_document_id": false }, + { + "source_path": "aspnetcore/fundamentals/openapi/buildtime-openapi.md", + "redirect_url": "/aspnet/core/fundamentals/openapi/aspnetcore-openapi#generate-openapi-documents-at- build-time", + "redirect_document_id": false + }, { "source_path": "aspnetcore/security/authentication/samples.md", "redirect_url": "/aspnet/core/security/authorization/introduction", diff --git a/aspnetcore/blazor/components/quickgrid.md b/aspnetcore/blazor/components/quickgrid.md index 4db05296429e..ba5a5d97e45a 100644 --- a/aspnetcore/blazor/components/quickgrid.md +++ b/aspnetcore/blazor/components/quickgrid.md @@ -313,7 +313,7 @@ The conve ```razor @page "/food-recalls" @inject HttpClient Http -@inject NavigationManager NavManager +@inject NavigationManager Navigation Food Recalls @@ -332,14 +332,14 @@ The conve

Total: @numResults results found

@code { - GridItemsProvider? foodRecallProvider; - int numResults; + private GridItemsProvider? foodRecallProvider; + private int numResults; protected override async Task OnInitializedAsync() { foodRecallProvider = async req => { - var url = NavManager.GetUriWithQueryParameters( + var url = Navigation.GetUriWithQueryParameters( "https://api.fda.gov/food/enforcement.json", new Dictionary { diff --git a/aspnetcore/blazor/components/render-modes.md b/aspnetcore/blazor/components/render-modes.md index 27edc9d1f35a..8b47449b0dbb 100644 --- a/aspnetcore/blazor/components/render-modes.md +++ b/aspnetcore/blazor/components/render-modes.md @@ -688,7 +688,7 @@ The `Shared` folder maintains the `AccountLayout` layout component. The componen ```razor @inherits LayoutComponentBase @layout BlazorSample.Components.Layout.MainLayout -@inject NavigationManager NavigationManager +@inject NavigationManager Navigation @if (HttpContext is null) { @@ -707,7 +707,7 @@ else { if (HttpContext is null) { - NavigationManager.Refresh(forceReload: true); + Navigation.Refresh(forceReload: true); } } } @@ -756,7 +756,7 @@ The app has a custom layout that can be applied to components around the app. Us ```razor @inherits LayoutComponentBase @layout MainLayout -@inject NavigationManager NavigationManager +@inject NavigationManager Navigation @if (HttpContext is null) { @@ -775,7 +775,7 @@ else { if (HttpContext is null) { - NavigationManager.Refresh(forceReload: true); + Navigation.Refresh(forceReload: true); } } } diff --git a/aspnetcore/blazor/fundamentals/index.md b/aspnetcore/blazor/fundamentals/index.md index d2e6c89db74c..12bc63c6c5fd 100644 --- a/aspnetcore/blazor/fundamentals/index.md +++ b/aspnetcore/blazor/fundamentals/index.md @@ -178,22 +178,6 @@ Documentation sample apps are available for inspection and download: Locate a sample app by first selecting the version folder that matches the version of .NET that you're working with. -:::moniker range=">= aspnetcore-8.0" - -Samples apps in the repository: - -* Blazor Web App -* Blazor WebAssembly -* Blazor Web App with EF Core () -* Blazor Web App with SignalR () -* Two Blazor Web Apps and a Blazor WebAssembly app for calling web (server) APIs () -* Blazor Web App with OIDC (BFF and non-BFF patterns) () -* Blazor WebAssembly scopes-enabled logging () -* Blazor WebAssembly with ASP.NET Core Identity () -* .NET MAUI Blazor Hybrid app with a Blazor Web App and a shared UI provided by a Razor class library (RCL) () - -:::moniker-end - :::moniker range="< aspnetcore-8.0" The sample repo contains two types of samples: @@ -206,7 +190,7 @@ The sample repo contains two types of samples: :::moniker-end -For more information, see the [Blazor samples GitHub repository README.md file](https://github.com/dotnet/blazor-samples). +For more information and a list of the samples in the repository, see the [Blazor samples GitHub repository README.md file](https://github.com/dotnet/blazor-samples). The ASP.NET Core repository's Basic Test App is also a helpful set of samples for various Blazor scenarios: diff --git a/aspnetcore/blazor/security/webassembly/hosted-with-identity-server.md b/aspnetcore/blazor/security/webassembly/hosted-with-identity-server.md index d5425f6fb4dc..dfda545a866e 100644 --- a/aspnetcore/blazor/security/webassembly/hosted-with-identity-server.md +++ b/aspnetcore/blazor/security/webassembly/hosted-with-identity-server.md @@ -133,6 +133,8 @@ The following services are registered. .AddEntityFrameworkStores(); ``` + [!INCLUDE[](~/blazor/security/includes/secure-authentication-flows.md)] + * Identity Server with an additional helper method that sets up default ASP.NET Core conventions on top of Identity Server: ```csharp diff --git a/aspnetcore/fundamentals/openapi/aspnetcore-openapi.md b/aspnetcore/fundamentals/openapi/aspnetcore-openapi.md index 1462553f00ac..d047c39d3d08 100644 --- a/aspnetcore/fundamentals/openapi/aspnetcore-openapi.md +++ b/aspnetcore/fundamentals/openapi/aspnetcore-openapi.md @@ -41,25 +41,6 @@ dotnet add package Microsoft.AspNetCore.OpenApi --prerelease ``` --- -To add support for generating OpenAPI documents at build time, install the `Microsoft.Extensions.ApiDescription.Server` package: - -### [Visual Studio](#tab/visual-studio) - -Run the following command from the **Package Manager Console**: - - ```powershell - Install-Package Microsoft.Extensions.ApiDescription.Server -IncludePrerelease -``` - -### [.NET CLI](#tab/net-cli) - -Run the following command in the directory that contains the project file: - -```dotnetcli -dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease -``` ---- - ## Configure OpenAPI document generation The following code: @@ -71,528 +52,6 @@ The following code: Launch the app and navigate to `https://localhost:/openapi/v1.json` to view the generated OpenAPI document. -## Including OpenAPI metadata in an ASP.NET web app - -### Including OpenAPI metadata for endpoints - -ASP.NET collects metadata from the web app's endpoints and uses it to generate an OpenAPI document. -In controller-based apps, metadata is collected from attributes like [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute), [`[HttpPost]`](xref:Microsoft.AspNetCore.Mvc.HttpPostAttribute), -and [`[Produces]`](xref:Microsoft.AspNetCore.Mvc.ProducesAttribute). -In minimal APIs, metadata can be collected from attributes, but may also be set by using extension methods -and other strategies, such as returning from route handlers. -The following table provides an overview of the metadata collected and the strategies for setting it. - -| Metadata | Attribute | Extension method | Other strategies | -| --- | --- | --- | -| summary | [`[EndpointSummary]`](xref:Microsoft.AspNetCore.Http.EndpointSummaryAttribute) | | | -| description | [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) | | | -| tags | [`[Tags]`](xref:Microsoft.AspNetCore.Http.TagsAttribute) | | | -| operationId | [`[EndpointName]`](xref:Microsoft.AspNetCore.Routing.EndpointNameAttribute) | | | -| parameters | [`[FromQuery]`](xref:Microsoft.AspNetCore.Mvc.FromQueryAttribute), [`[FromRoute]`](xref:Microsoft.AspNetCore.Mvc.FromRouteAttribute), [`[FromHeader]`](xref:Microsoft.AspNetCore.Mvc.FromHeaderAttribute), [`[FromForm]`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) | | -| parameter description | [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) | | | -| requestBody | [`[FromBody]`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) | | | -| responses | [`[Produces]`](xref:Microsoft.AspNetCore.Mvc.ProducesAttribute) | , | | -| Excluding endpoints | [`[ExcludeFromDescription]`](xref:Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute), [`[ApiExplorerSettings]`](xref:Microsoft.AspNetCore.Mvc.ApiExplorerSettingsAttribute) | | | - -ASP.NET Core does not collect metadata from XML doc comments. - -The following sections demonstrate how to include metadata in an app to customize the generated OpenAPI document. - -#### Summary and description - -The endpoint summary and description can be set using the [`[EndpointSummary]`](xref:Microsoft.AspNetCore.Http.EndpointSummaryAttribute) and [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) attributes, -or in minimal APIs, using the and extension methods. - -##### [Minimal APIs](#tab/minimal-apis) - -The following sample demonstrates the different strategies for setting summaries and descriptions. - -Note that the attributes are placed on the delegate method and not on the app.MapGet method. - -```csharp -app.MapGet("/extension-methods", () => "Hello world!") - .WithSummary("This is a summary.") - .WithDescription("This is a description."); - -app.MapGet("/attributes", - [EndpointSummary("This is a summary.")] - [EndpointDescription("This is a description.")] - () => "Hello world!"); -``` - -##### [Controllers](#tab/controllers) - -The following sample demonstrates how to set summaries and descriptions. - -```csharp - [EndpointSummary("This is a summary.")] - [EndpointDescription("This is a description.")] - [HttpGet("attributes")] - public IResult Attributes() - { - return Results.Ok("Hello world!"); - } -``` ---- - -#### tags - -OpenAPI supports specifying tags on each endpoint as a form of categorization. - -##### [Minimal APIs](#tab/minimal-apis) - -In minimal APIs, tags can be set using either the [`[Tags]`](xref:Microsoft.AspNetCore.Http.TagsAttribute) attribute or the extension method. - -The following sample demonstrates the different strategies for setting tags. - -```csharp -app.MapGet("/extension-methods", () => "Hello world!") - .WithTags("todos", "projects"); - -app.MapGet("/attributes", - [Tags("todos", "projects")] - () => "Hello world!"); -``` - -##### [Controllers](#tab/controllers) - -In controller-based apps, the controller name is automatically added as a tag on each of its endpoints, but this can be overridden using the [`[Tags]`](xref:Microsoft.AspNetCore.Http.TagsAttribute) attribute. - -The following sample demonstrates how to set tags. - -```csharp - [Tags(["todos", "projects"])] - [HttpGet("attributes")] - public IResult Attributes() - { - return Results.Ok("Hello world!"); - } -``` ---- - -#### operationId - -OpenAPI supports an operationId on each endpoint as a unique identifier or name for the operation. - -##### [Minimal APIs](#tab/minimal-apis) - -In minimal APIs, the operationId can be set using either the [`[EndpointName]`](xref:Microsoft.AspNetCore.Routing.EndpointNameAttribute) attribute or the extension method. - -The following sample demonstrates the different strategies for setting the operationId. - -```csharp -app.MapGet("/extension-methods", () => "Hello world!") - .WithName("FromExtensionMethods"); - -app.MapGet("/attributes", - [EndpointName("FromAttributes")] - () => "Hello world!"); -``` - -##### [Controllers](#tab/controllers) - -In controller-based apps, the operationId can be set using the [`[EndpointName]`](xref:Microsoft.AspNetCore.Routing.EndpointNameAttribute) attribute. - -The following sample demonstrates how to set the operationId. - -```csharp - [EndpointName("FromAttributes")] - [HttpGet("attributes")] - public IResult Attributes() - { - return Results.Ok("Hello world!"); - } -``` ---- - -#### parameters - -OpenAPI supports annotating path, query string, header, and cookie parameters that are consumed by an API. - -The framework infers the types for request parameters automatically based on the signature of the route handler. - -The [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) attribute can be used to provide a description for a parameter. - -##### [Minimal APIs](#tab/minimal-apis) - -The follow sample demonstrates how to set a description for a parameter. - -```csharp -app.MapGet("/attributes", - ([Description("This is a description.")] string name) => "Hello world!"); -``` - -##### [Controllers](#tab/controllers) - -The following sample demonstrates how to set a description for a parameter. - -```csharp - [HttpGet("attributes")] - public IResult Attributes([Description("This is a description.")] string name) - { - return Results.Ok("Hello world!"); - } -``` ---- - -#### Describe the request body - -The `requestBody` field in OpenAPI describes the body of a request that an API client can send to the server, including the content type(s) supported and the schema for the body content. - -When the endpoint handler method accepts parameters that are bound from the request body, ASP.NET Core generates a corresponding `requestBody` for the operation in the OpenAPI document. Metadata for the request body can also be specified using attributes or extension methods. Additional metadata can be set with a [document transformer](#use-document-transformers) or [operation transformer](#use-operation-transformers). - -If the endpoint doesn't define any parameters bound to the request body, but instead consumes the request body from the directly, ASP.NET Core provides mechanisms to specify request body metadata. This is a common scenario for endpoints that process the request body as a stream. - -Some request body metadata can be determined from the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) or [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) parameters of the route handler method. - -A description for the request body can be set with a [`[Description]`](xref:System.ComponentModel.DescriptionAttribute) attribute on the parameter with [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) or [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute). - -If the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) parameter is non-nullable and is not set to in the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) attribute, the request body is required and the `required` field of the `requestBody` is set to `true` in the generated OpenAPI document. -Form bodies are always required and have `required` set to `true`. - -Use a [document transformer](#use-document-transformers) or an [operation transformer](#use-operation-transformers) to set the `example`, `examples`, or `encoding` fields, or to add specification extensions for the request body in the generated OpenAPI document. - -Other mechanisms for setting request body metadata depend on the type of app being developed and are described in the following sections. - -##### [Minimal APIs](#tab/minimal-apis) - -The content types for the request body in the generated OpenAPI document are determined from the type of the parameter that is bound to the request body or specified with the extension method. -By default, the content type of a [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) parameter will be `application/json` and the content type for [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) parameter(s) will be `multipart/form-data` or `application/x-www-form-urlencoded`. - -Support for these default content types is built in to Minimal APIs, and other content types can be handled by using custom binding. -See the [Custom binding](xref:fundamentals/minimal-apis/parameter-binding#custom-binding) topic of the Minimal APIs documentation for more information. - -There are several ways to specify a different content type for the request body. -If the type of the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) parameter implements , ASP.NET Core uses this interface to determine the content type(s) in the request body. -The framework uses the method of this interface to set the content type(s) and type of the body content of the request body. For example, a `Todo` class that accepts either `application/xml` or `text/xml` content-type can use to provide this information to the framework. - -```csharp -public class Todo : IEndpointParameterMetadataProvider -{ - public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder) - { - builder.Metadata.Add(new AcceptsMetadata(["application/xml", "text/xml"], typeof(Todo))); - } -} -``` - -The extension method can also be used to specify the content type of the request body. -In the following example, the endpoint accepts a `Todo` object in the request body with an expected content-type of `application/xml`. - -```csharp -app.MapPut("/todos/{id}", (int id, Todo todo) => ...) - .Accepts("application/xml"); -``` - -Since `application/xml` is not a built-in content type, the `Todo` class must implement the interface to provide a custom binding for the request body. For example: - -```csharp -public class Todo : IBindableFromHttpContext -{ - public static async ValueTask BindAsync(HttpContext context, ParameterInfo parameter) - { - var xmlDoc = await XDocument.LoadAsync(context.Request.Body, LoadOptions.None, context.RequestAborted); - var serializer = new XmlSerializer(typeof(Todo)); - return (Todo?)serializer.Deserialize(xmlDoc.CreateReader()); - } -``` - -If the endpoint doesn't define any parameters bound to the request body, use the extension method to specify the content type that the endpoint accepts. - -If you specify multiple times, only metadata from the last one is used -- they aren't combined. - -##### [Controllers](#tab/controllers) - -In controller-based apps, the content type(s) for the request body in the generated OpenAPI document are determined from the type of the parameter that is bound to the request body, the types configured in the application, or by a [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute on the route handler method. - -ASP.NET Core uses an to deserialize a [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) request body. -InputFormatters are configured in the passed to the extension method for the app's service collection. -Each input formatter declares the content types it can handle, in its property, and the type(s) of body content it can handle, with its method. - -ASP.NET Core MVC includes built-in input formatters for JSON and XML, though only the JSON input formatter is enabled by default. -The built-in JSON input formatter supports the `application/json`, `text/json`, and `application/*+json` content types, and the built-in XML input formatter supports the `application/xml`, `text/xml`, and `application/*+xml` content types. - -By default, the content type of a [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) request body may be any content type accepted by an for the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) parameter type. For a request body with [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) parameter(s) the default content types are `multipart/form-data` or `application/x-www-form-urlencoded`.These content types will be included in the generated OpenAPI document if the [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute is not specified on the route handler method. - -The content type(s) accepted by a route handler can be restricted using a [filter](xref:mvc/controllers/filters) on the endpoint (action scope). -The [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute adds an action scope filter to the endpoint that restricts the content types that a route handler will accept. -In this case, the requestBody in the generated OpenAPI document will include only the content type(s) specified in the [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute. - -A [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute can't add support for a content type that doesn't have an associated input formatter, and the generated OpenAPI document doesn't include any content types that don't have an associated input formatter. - -For content types other than JSON or XML, you need to create a custom input formatter. -For more information and examples, see [Custom formatters in ASP.NET Core Web API](xref:web-api/advanced/custom-formatters). - -If the route handler doesn't have a [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) or [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) parameter, the route handler might read the request body directly from the `Request.Body` stream and might use the [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute to restrict the content types allowed, but no requestBody is generated in the OpenAPI document. - ---- - -#### Describe response types - -OpenAPI supports providing a description of the responses returned from an API. ASP.NET Core provides several strategies for setting the response metadata of an endpoint. Response metadata that can be set includes the status code, the type of the response body, and content type(s) of a response. Responses in OpenAPI may have additional metadata, such as description, headers, links, and examples. This additional metadata can be set with a [document transformer](#use-document-transformers) or [operation transformer](#use-operation-transformers). - -The specific mechanisms for setting response metadata depend on the type of app being developed. - -##### [Minimal APIs](#tab/minimal-apis) - -In Minimal API apps, ASP.NET Core can extract the response metadata added by extension methods on the endpoint, attributes on the route handler, and the return type of the route handler. - -* The extension method can be used on the endpoint to specify the status code, the type of the response body, and content type(s) of a response from an endpoint. -* The [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) or attribute can be used to specify the type of the response body. -* A route handler can be used to return a type that implements to specify the type and content-type(s) of the response body. -* The extension method on the endpoint can be used to specify the status code and content-type(s) of an error response. - -Note that the and extension methods are supported on both and on . This allows, for example, a common set of error responses to be defined for all operations in a group. - -When not specified by one of the preceding strategies, the: -* Status code for the response defaults to 200. -* Schema for the response body can be inferred from the implicit or explicit return type of the endpoint method, for example, from `T` in ; otherwise, it's considered to be unspecified. -* Content-type for the specified or inferred response body is "application/json". - -In Minimal APIs, the extension method and the [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) attribute only set the response metadata for the endpoint. They do not modify or constrain the behavior of the endpoint, which may return a different status code or response body type than specified by the metadata, and the content-type is determined by the return type of the route handler method, irrespective of any content-type specified in attributes or extension methods. - -The extension method can specify an endpoint's response type, with a default status code of 200 and a default content type of `application/json`. The following example illustrates this: - -```csharp -app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync()) - .Produces>(); -``` - -The [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) can be used to add response metadata to an endpoint. Note that the attribute is applied to the route handler method, not the method invocation to create the route, as shown in the following example: - -```csharp -app.MapGet("/todos", - [ProducesResponseType>(200)] - async (TodoDb db) => await db.Todos.ToListAsync()); -``` - -Using in the implementation of an endpoint's route handler automatically includes the response type metadata for the endpoint. For example, the following code automatically annotates the endpoint with a response under the `200` status code with an `application/json` content type. - -```csharp -app.MapGet("/todos", async (TodoDb db) => -{ - var todos = await db.Todos.ToListAsync(); - return TypedResults.Ok(todos); -}); -``` - -Only return types that implement create a `responses` entry in the OpenAPI document. The following is a partial list of some of the helper methods that produce a `responses` entry: - -| TypedResults helper method | status code | -| -------------------------- | ----------- | -| Ok() | 200 | -| Created() | 201 | -| CreatedAtRoute() | 201 | -| Accepted() | 202 | -| AcceptedAtRoute() | 202 | -| NoContent() | 204 | -| BadRequest() | 400 | -| ValidationProblem() | 400 | -| NotFound() | 404 | -| Conflict() | 409 | -| UnprocessableEntity() | 422 | - -All of these methods except `NoContent` have a generic overload that specifies the type of the response body. - -A class can be implemented to set the endpoint metadata and return it from the route handler. - -###### Set responses for `ProblemDetails` - -When setting the response type for endpoints that may return a ProblemDetails response, the following can be used to add the appropriate response metadata for the endpoint: - -* -* extension method. -* with a status code in the (400-499) range. - -For more information on how to configure a Minimal API app to return ProblemDetails responses, see . - -###### Multiple response types - -If an endpoint can return different response types in different scenarios, you can provide metadata in the following ways: - -* Call the extension method multiple times, as shown in the following example: - - [!code-csharp[](~/fundamentals/minimal-apis/samples/todo/Program.cs?name=snippet_getCustom)] - -* Use in the signature and in the body of the handler, as shown in the following example: - - :::code language="csharp" source="~/../AspNetCore.Docs.Samples/fundamentals/minimal-apis/samples/MultipleResultTypes/Program.cs" id="snippet_multiple_result_types"::: - - The `Results` [union types](https://en.wikipedia.org/wiki/Union_type) declare that a route handler returns multiple `IResult`-implementing concrete types, and any of those types that implement will contribute to the endpoint’s metadata. - - The union types implement implicit cast operators. These operators enable the compiler to automatically convert the types specified in the generic arguments to an instance of the union type. This capability has the added benefit of providing compile-time checking that a route handler only returns the results that it declares it does. Attempting to return a type that isn't declared as one of the generic arguments to `Results` results in a compilation error. - -##### [Controllers](#tab/controllers) - -In controller-based apps, ASP.NET Core can extract the response metadata from the action method signature, attributes, and conventions. - -* You can use the [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) or attribute to specify the status code, the type of the response body, and content type(s) of a response from an action method. -* You can use the [`[Produces]`](xref:Microsoft.AspNetCore.Mvc.ProducesAttribute) or attribute to specify the type of the response body. -* You can use the [`[ProducesDefaultResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesDefaultResponseTypeAttribute) attribute to specify the response body type for the "default" response. -* You can use the [`[ProducesErrorResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesErrorResponseTypeAttribute) attribute to specify the response body type for an error response. However, be aware that this is only complements the status code and content type(s) specified by an [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) attribute with a 4XX status code. - -Only one [`[Produces]`](xref:Microsoft.AspNetCore.Mvc.ProducesAttribute) or attributes may be applied to an action method, but multiple [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) or attributes with different status codes may be applied to a single action method. - -All of the above attributes can be applied to individual action methods or to the controller class where it applies to all action methods in the controller. - -When not specified by an attribute: -* the status code for the response defaults to 200, -* the schema for the response body of 2xx responses may be inferred from the return type of the action method, e.g. from `T` in , but otherwise is considered to be not specified, -* the schema for the response body of 4xx responses is inferred to be a problem details object, -* the schema for the response body of 3xx and 5xx responses is considered to be not specified, -* the content-type for the response body can be inferred from the return type of the action method and the set of output formatters. - -Note that there are no compile-time checks to ensure that the response metadata specified with a [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) attribute is consistent with the actual behavior of the action method, -which may return a different status code or response body type than specified by the metadata. - -In controller-based apps, ASP.NET responds with a ProblemDetails response type when model validation fails or when the action method returns a result with a 4xx or 5xx HTTP status code. Validation errors typically use the 400 status code, so you can use the [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) attribute to specify the error response for an action, as shown in the following example: - -```csharp -[HttpPut("/todos/{id}")] -[ProducesResponseType(StatusCodes.Status200OK, "application/json")] -[ProducesResponseType(StatusCodes.Status201Created, "application/json")] -[ProducesResponseType(StatusCodes.Status400BadRequest, "application/problem+json")] -public async Task> CreateOrReplaceTodo(string id, Todo todo) -``` - -This example also illustrates how to define multiple response types for an action method, including the content type of the response body. - ---- - -#### Excluding endpoints from the generated document - -By default, all endpoints that are defined in an app are documented in the generated OpenAPI file, but endpoints can be excluded from the document using attributes or extension methods. - -The mechanism for specifying an endpoint that should be excluded depends on the type of app being developed. - -##### [Minimal APIs](#tab/minimal-apis) - -Minimal APIs support two strategies for excluding a given endpoint from the OpenAPI document: - -* extension method -* [`[ExcludeFromDescription]`](xref:Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute) attribute - -The following sample demonstrates the different strategies for excluding a given endpoint from the generated OpenAPI document. - -```csharp -app.MapGet("/extension-method", () => "Hello world!") - .ExcludeFromDescription(); - -app.MapGet("/attributes", - [ExcludeFromDescription] - () => "Hello world!"); -``` - -##### [Controllers](#tab/controllers) - -In controller-based apps, the [`[ApiExplorerSettings]`](xref:Microsoft.AspNetCore.Mvc.ApiExplorerSettingsAttribute) attribute can be used to exclude an endpoint or all endpoints in a controller class from the OpenAPI document. - -The following example demonstrates how to exclude an endpoint from the generated OpenAPI document: - -```csharp - [HttpGet("/private")] - [ApiExplorerSettings(IgnoreApi = true)] - public IActionResult PrivateEndpoint() { - return Ok("This is a private endpoint"); - } -``` ---- - -### Including OpenAPI metadata for data types - -C# classes or records used in request or response bodies are represented as schemas -in the generated OpenAPI document. -By default, only public properties are represented in the schema, but there are - to also create schema properties for fields. - -When the is set to camel-case (this is the default -in ASP.NET web applications), property names in a schema are the camel-case form -of the class or record property name. -The [`[JsonPropertyName]`](xref:System.Text.Json.Serialization.JsonPropertyNameAttribute) can be used on an individual property to specify the name -of the property in the schema. - -#### type and format - -The JSON Schema library maps standard C# types to OpenAPI `type` and `format` as follows: - -| C# Type | OpenAPI `type` | OpenAPI `format` | -| -------------- | -------------- | ---------------- | -| int | integer | int32 | -| long | integer | int64 | -| short | integer | int16 | -| byte | integer | uint8 | -| float | number | float | -| double | number | double | -| decimal | number | double | -| bool | boolean | | -| string | string | | -| char | string | char | -| byte[] | string | byte | -| DateTimeOffset | string | date-time | -| DateOnly | string | date | -| TimeOnly | string | time | -| Uri | string | uri | -| Guid | string | uuid | -| object | _omitted_ | | -| dynamic | _omitted_ | | - -Note that object and dynamic types have _no_ type defined in the OpenAPI because these can contain data of any type, including primitive types like int or string. - -The `type` and `format` can also be set with a [Schema Transformer](#use-schema-transformers). For example, you may want the `format` of decimal types to be `decimal` instead of `double`. - -#### Using attributes to add metadata - -ASP.NET uses metadata from attributes on class or record properties to set metadata on the corresponding properties of the generated schema. - -The following table summarizes attributes from the `System.ComponentModel` namespace that provide metadata for the generated schema: - -| Attribute | Description | -| ---------------------------- | ----------- | -| [`[Description]`](xref:System.ComponentModel.DescriptionAttribute) | Sets the `description` of a property in the schema. | -| [`[Required]`](xref:System.ComponentModel.DataAnnotations.RequiredAttribute) | Marks a property as `required` in the schema. | -| [`[DefaultValue]`](xref:System.ComponentModel.DefaultValueAttribute) | Sets the `default` value of a property in the schema. | -| [`[Range]`](xref:System.ComponentModel.DataAnnotations.RangeAttribute) | Sets the `minimum` and `maximum` value of an integer or number. | -| [`[MinLength]`](xref:System.ComponentModel.DataAnnotations.MinLengthAttribute) | Sets the `minLength` of a string. | -| [`[MaxLength]`](xref:System.ComponentModel.DataAnnotations.MaxLengthAttribute) | Sets the `maxLength` of a string. | -| [`[RegularExpression]`](xref:System.ComponentModel.DataAnnotations.RegularExpressionAttribute) | Sets the `pattern` of a string. | - -Note that in controller-based apps, these attributes add filters to the operation to validate that any incoming data satisfies the constraints. In Minimal APIs, these attributes set the metadata in the generated schema but validation must be performed explicitly via an endpoint filter, in the route handler's logic, or via a third-party package. - -#### Other sources of metadata for generated schemas - -##### required - -Properties can also be marked as `required` with the [required](/dotnet/csharp/language-reference/proposals/csharp-11.0/required-members#required-modifier) modifier. - -##### enum - -Enum types in C# are integer-based, but can be represented as strings in JSON with a [`[JsonConverter]`](xref:System.Text.Json.Serialization.JsonConverterAttribute) and a . When an enum type is represented as a string in JSON, the generated schema will have an `enum` property with the string values of the enum. -An enum type without a [`[JsonConverter]`](xref:System.Text.Json.Serialization.JsonConverterAttribute) will be defined as `type: integer` in the generated schema. - -**Note:** The [`[AllowedValues]`](xref:System.ComponentModel.DataAnnotations.AllowedValuesAttribute) does not set the `enum` values of a property. - -##### nullable - -Properties defined as a nullable value or reference type have `nullable: true` in the generated schema. This is consistent with the default behavior of the deserializer, which accepts `null` as a valid value for a nullable property. - -##### additionalProperties - -Schemas are generated without an `additionalProperties` assertion by default, which implies the default of `true`. This is consistent with the default behavior of the deserializer, which silently ignores additional properties in a JSON object. - -If the additional properties of a schema should only have values of a specific type, define the property or class as a `Dictionary`. The key type for the dictionary must be `string`. This generates a schema with `additionalProperties` specifying the schema for "type" as the required value types. - -##### Metadata for polymorphic types - -Use the [`[JsonPolymorphic]`](xref:System.Text.Json.Serialization.JsonPolymorphicAttribute) and [`[JsonDerivedType]`](xref:System.Text.Json.Serialization.JsonDerivedTypeAttribute) attributes on a parent class to to specify the discriminator field and subtypes for a polymorphic type. - -The [`[JsonDerivedType]`](xref:System.Text.Json.Serialization.JsonDerivedTypeAttribute) adds the discriminator field to the schema for each subclass, with an enum specifying the specific discriminator value for the subclass. This attribute also modifies the constructor of each derived class to set the discriminator value. - -An abstract class with a [`[JsonPolymorphic]`](xref:System.Text.Json.Serialization.JsonPolymorphicAttribute) attribute has a `discriminator` field in the schema, but a concrete class with a [`[JsonPolymorphic]`](xref:System.Text.Json.Serialization.JsonPolymorphicAttribute) attribute doesn't have a `discriminator` field. OpenAPI requires that the discriminator property be a required property in the schema, but since the discriminator property isn't defined in the concrete base class, the schema cannot include a `discriminator` field. - -#### Adding metadata with a schema transformer - -A schema transformer can be used to override any default metadata or add additional metadata, such as `example` values, to the generated schema. See [Use schema transformers](#use-schema-transformers) for more information. - ## Options to Customize OpenAPI document generation The following sections demonstrate how to customize OpenAPI document generation. @@ -657,107 +116,95 @@ The OpenAPI document is regenerated every time a request to the OpenAPI endpoint [!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_mapopenapiwithcaching)] - - -## OpenAPI document transformers - -This section demonstrates how to customize OpenAPI documents with transformers. - -### Customize OpenAPI documents with transformers - -Transformers provide an API for modifying the OpenAPI document with user-defined customizations. Transformers are useful for scenarios like: - -* Adding parameters to all operations in a document. -* Modifying descriptions for parameters or operations. -* Adding top-level information to the OpenAPI document. +## Generate OpenAPI documents at build-time -Transformers fall into three categories: +In typical web applications, OpenAPI documents are generated at run-time and served via an HTTP request to the application server. -* Document transformers have access to the entire OpenAPI document. These can be used to make global modifications to the document. -* 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. -* 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. +In some scenarios, it's helpful to generate the OpenAPI document during the application's build step. These scenarios include: -Transformers can be registered onto the document by calling the method on the object. The following snippet shows different ways to register transformers onto the document: +- Generating OpenAPI documentation that is committed into source control. +- Generating OpenAPI documentation that is used for spec-based integration testing. +- Generating OpenAPI documentation that is served statically from the web server. -* Register a document transformer using a delegate. -* Register a document transformer using an instance of . -* Register a document transformer using a DI-activated . -* Register an operation transformer using a delegate. -* Register an operation transformer using an instance of . -* Register an operation transformer using a DI-activated . -* Register a schema transformer using a delegate. -* Register a schema transformer using an instance of . -* Register a schema transformer using a DI-activated . - -[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_transUse&highlight=8-19)] - -### Execution order for transformers - -Transformers execute in first-in first-out order based on registration. In the following snippet, the document transformer has access to the modifications made by the operation transformer: - -[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_transInOut&highlight=3-9)] +To add support for generating OpenAPI documents at build time, install the `Microsoft.Extensions.ApiDescription.Server` package: -### Use document transformers +### [Visual Studio](#tab/visual-studio) -Document transformers have access to a context object that includes: +Run the following command from the **Package Manager Console**: -* The name of the document being modified. -* The associated with that document. -* The used in document generation. + ```powershell + Install-Package Microsoft.Extensions.ApiDescription.Server -IncludePrerelease +``` -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. +### [.NET CLI](#tab/net-cli) -[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_documenttransformer1)] +Run the following command in the directory that contains the project file: -Service-activated document transformers can utilize instances from DI to modify the app. The following sample demonstrates a document transformer that uses the 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: +```dotnetcli +dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease +``` +--- -[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_documenttransformer2)] +Upon installation, this package will automatically generate the Open API document(s) associated with the application during build and populate them into the application's output directory. -Document transformers are unique to the document instance they're associated with. In the following example, a transformer: +```cli +$ dotnet build +$ cat bin/Debug/net9.0/{ProjectName}.json +``` -* Registers authentication-related requirements to the `internal` document. -* Leaves the `public` document unmodified. +### Customizing build-time document generation -[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_multidoc_operationtransformer1)] +#### Modifying the output directory of the generated Open API file -### Use operation transformers +By default, the generated OpenAPI document will be emitted to the application's output directory. To modify the location of the emitted file, set the target path in the `OpenApiDocumentsDirectory` property. -Operations are unique combinations of HTTP paths and methods in an OpenAPI document. Operation transformers are helpful when a modification: +```xml + + ./ + +``` -* Should be made to each endpoint in an app, or -* Conditionally applied to certain routes. +The value of `OpenApiDocumentsDirectory` is resolved relative to the project file. Using the `./` value above will emit the OpenAPI document in the same directory as the project file. -Operation transformers have access to a context object which contains: +#### Modifying the output file name -* The name of the document the operation belongs to. -* The associated with the operation. -* The used in document generation. +By default, the generated OpenAPI document will have the same name as the application's project file. To modify the name of the emitted file, set the `--file-name` argument in the `OpenApiGenerateDocumentsOptions` property. -For example, the following operation transformer adds `500` as a response status code supported by all operations in the document. +```xml + + --file-name my-open-api + +``` -[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_operationtransformer1)] +#### Selecting the OpenAPI document to generate -### Use schema transformers +Some applications may be configured to emit multiple OpenAPI documents, for various versions of an API or to distinguish between public and internal APIs. By default, the build-time document generator will emit files for all documents that are configured in an application. To only emit for a single document name, set the `--document-name` argument in the `OpenApiGenerateDocumentsOptions` property. -Schemas are the data models that are used in request and response bodies in an OpenAPI document. Schema transformers are useful when a modification: +```xml + + --document-name v2 + +``` -* Should be made to each schema in the document, or -* Conditionally applied to certain schemas. +### Customizing run-time behavior during build-time document generation -Schema transformers have access to a context object which contains: +Under the hood, build-time OpenAPI document generation functions by launching the application's entrypoint with an inert server implementation. This is a requirement to produce accurate OpenAPI documents since all information in the OpenAPI document cannot be statically analyzed. Because the application's entrypoint is invoked, any logic in the applications' startup will be invoked. This includes code that injects services into the DI container or reads from configuration. In some scenarios, it's necessary to restrict the codepaths that will run when the application's entry point is being invoked from build-time document generation. These scenarios include: -* The name of the document the schema belongs to. -* The JSON type information associated with the target schema. -* The used in document generation. +- Not reading from certain configuration strings. +- Not registering database-related services. -For example, the following schema transformer sets the `format` of decimal types to `decimal` instead of `double`: +In order to restrict these codepaths from being invoked by the build-time generation pipeline, they can be conditioned behind a check of the entry assembly like so: -[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_schematransformer1)] +```csharp +using System.Reflection; -## Additional resources +var builder = WebApplication.CreateBuilder(); -* -* [OpenAPI specification](https://spec.openapis.org/oas/v3.0.3) +if (Assembly.GetEntryAssembly()?.GetName().Name != "GetDocument.Insider") +{ + builder.Services.AddDefaults(); +} +``` ::: moniker-end diff --git a/aspnetcore/fundamentals/openapi/buildtime-openapi.md b/aspnetcore/fundamentals/openapi/buildtime-openapi.md deleted file mode 100644 index caabedf805d9..000000000000 --- a/aspnetcore/fundamentals/openapi/buildtime-openapi.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: Generate OpenAPI documents at build time -author: captainsafia -description: Learn how to generate OpenAPI documents in your application's build step -ms.author: safia -monikerRange: '>= aspnetcore-6.0' -ms.custom: mvc -ms.date: 8/13/2024 -uid: fundamentals/openapi/buildtime-openapi ---- - -# Generate OpenAPI documents at build-time - -In typical web applications, OpenAPI documents are generated at run-time and served via an HTTP request to the application server. - -In some scenarios, it's helpful to generate the OpenAPI document during the application's build step. These scenarios include: - -- Generating OpenAPI documentation that is committed into source control. -- Generating OpenAPI documentation that is used for spec-based integration testing. -- Generating OpenAPI documentation that is served statically from the web server. - -To add support for generating OpenAPI documents at build time, install the `Microsoft.Extensions.ApiDescription.Server` package: - -### [Visual Studio](#tab/visual-studio) - -Run the following command from the **Package Manager Console**: - - ```powershell - Install-Package Microsoft.Extensions.ApiDescription.Server -IncludePrerelease -``` - -### [.NET CLI](#tab/net-cli) - -Run the following command in the directory that contains the project file: - -```dotnetcli -dotnet add package Microsoft.Extensions.ApiDescription.Server --prerelease -``` ---- - -Upon installation, this package will automatically generate the Open API document(s) associated with the application during build and populate them into the application's output directory. - -```cli -$ dotnet build -$ cat bin/Debug/net9.0/{ProjectName}.json -``` - -## Customizing build-time document generation - -### Modifying the output directory of the generated Open API file - -By default, the generated OpenAPI document will be emitted to the application's output directory. To modify the location of the emitted file, set the target path in the `OpenApiDocumentsDirectory` property. - -```xml - - ./ - -``` - -The value of `OpenApiDocumentsDirectory` is resolved relative to the project file. Using the `./` value above will emit the OpenAPI document in the same directory as the project file. - -### Modifying the output file name - -By default, the generated OpenAPI document will have the same name as the application's project file. To modify the name of the emitted file, set the `--file-name` argument in the `OpenApiGenerateDocumentsOptions` property. - -```xml - - --file-name my-open-api - -``` - -### Selecting the OpenAPI document to generate - -Some applications may be configured to emit multiple OpenAPI documents, for various versions of an API or to distinguish between public and internal APIs. By default, the build-time document generator will emit files for all documents that are configured in an application. To only emit for a single document name, set the `--document-name` argument in the `OpenApiGenerateDocumentsOptions` property. - -```xml - - --document-name v2 - -``` - -## Customizing run-time behavior during build-time document generation - -Under the hood, build-time OpenAPI document generation functions by launching the application's entrypoint with an inert server implementation. This is a requirement to produce accurate OpenAPI documents since all information in the OpenAPI document cannot be statically analyzed. Because the application's entrypoint is invoked, any logic in the applications' startup will be invoked. This includes code that injects services into the DI container or reads from configuration. In some scenarios, it's necessary to restrict the codepaths that will run when the application's entry point is being invoked from build-time document generation. These scenarios include: - -- Not reading from certain configuration strings. -- Not registering database-related services. - -In order to restrict these codepaths from being invoked by the build-time generation pipeline, they can be conditioned behind a check of the entry assembly like so: - -```csharp -using System.Reflection; - -var builder = WebApplication.CreateBuilder(); - -if (Assembly.GetEntryAssembly()?.GetName().Name != "GetDocument.Insider") -{ - builder.Services.AddDefaults(); -} -``` diff --git a/aspnetcore/fundamentals/openapi/customize-openapi.md b/aspnetcore/fundamentals/openapi/customize-openapi.md new file mode 100644 index 000000000000..1fc84999e7b8 --- /dev/null +++ b/aspnetcore/fundamentals/openapi/customize-openapi.md @@ -0,0 +1,113 @@ +--- +title: Customize OpenAPI documents +author: captainsafia +description: Learn how to customize OpenAPI documents in an ASP.NET Core app +ms.author: safia +monikerRange: '>= aspnetcore-9.0' +ms.custom: mvc +ms.date: 10/26/2024 +uid: fundamentals/openapi/customize-openapi +--- +# Customize OpenAPI documents + + + +## OpenAPI document transformers + +This section demonstrates how to customize OpenAPI documents with transformers. + +### Customize OpenAPI documents with transformers + +Transformers provide an API for modifying the OpenAPI document with user-defined customizations. Transformers are useful for scenarios like: + +* Adding parameters to all operations in a document. +* Modifying descriptions for parameters or operations. +* Adding top-level information to the OpenAPI document. + +Transformers fall into three categories: + +* Document transformers have access to the entire OpenAPI document. These can be used to make global modifications to the document. +* 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. +* 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. + +Transformers can be registered onto the document by calling the method on the object. The following snippet shows different ways to register transformers onto the document: + +* Register a document transformer using a delegate. +* Register a document transformer using an instance of . +* Register a document transformer using a DI-activated . +* Register an operation transformer using a delegate. +* Register an operation transformer using an instance of . +* Register an operation transformer using a DI-activated . +* Register a schema transformer using a delegate. +* Register a schema transformer using an instance of . +* Register a schema transformer using a DI-activated . + +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_transUse&highlight=8-19)] + +### Execution order for transformers + +Transformers execute in first-in first-out order based on registration. In the following snippet, the document transformer has access to the modifications made by the operation transformer: + +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_transInOut&highlight=3-9)] + +### Use document transformers + +Document transformers have access to a context object that includes: + +* The name of the document being modified. +* The associated with that document. +* The used in document generation. + +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. + +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_documenttransformer1)] + +Service-activated document transformers can utilize instances from DI to modify the app. The following sample demonstrates a document transformer that uses the 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: + +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_documenttransformer2)] + +Document transformers are unique to the document instance they're associated with. In the following example, a transformer: + +* Registers authentication-related requirements to the `internal` document. +* Leaves the `public` document unmodified. + +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_multidoc_operationtransformer1)] + +### Use operation transformers + +Operations are unique combinations of HTTP paths and methods in an OpenAPI document. Operation transformers are helpful when a modification: + +* Should be made to each endpoint in an app, or +* Conditionally applied to certain routes. + +Operation transformers have access to a context object which contains: + +* The name of the document the operation belongs to. +* The associated with the operation. +* The used in document generation. + +For example, the following operation transformer adds `500` as a response status code supported by all operations in the document. + +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_operationtransformer1)] + +### Use schema transformers + +Schemas are the data models that are used in request and response bodies in an OpenAPI document. Schema transformers are useful when a modification: + +* Should be made to each schema in the document, or +* Conditionally applied to certain schemas. + +Schema transformers have access to a context object which contains: + +* The name of the document the schema belongs to. +* The JSON type information associated with the target schema. +* The used in document generation. + +For example, the following schema transformer sets the `format` of decimal types to `decimal` instead of `double`: + +[!code-csharp[](~/fundamentals/openapi/samples/9.x/WebMinOpenApi/Program.cs?name=snippet_schematransformer1)] + +## Additional resources + +* +* [OpenAPI specification](https://spec.openapis.org/oas/v3.0.3) diff --git a/aspnetcore/fundamentals/openapi/include-metadata.md b/aspnetcore/fundamentals/openapi/include-metadata.md new file mode 100644 index 000000000000..d4f33836ce44 --- /dev/null +++ b/aspnetcore/fundamentals/openapi/include-metadata.md @@ -0,0 +1,562 @@ +--- +title: Include OpenAPI metadata in an ASP.NET Core app +author: captainsafia +description: Learn how to add OpenAPI metadata in an ASP.NET Core app +ms.author: safia +monikerRange: '>= aspnetcore-9.0' +ms.custom: mvc +ms.date: 10/26/2024 +uid: fundamentals/openapi/include-metadata +--- +# Include OpenAPI metadata in an ASP.NET Core app + +## Include OpenAPI metadata for endpoints + +ASP.NET collects metadata from the web app's endpoints and uses it to generate an OpenAPI document. +In controller-based apps, metadata is collected from attributes like [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute), [`[HttpPost]`](xref:Microsoft.AspNetCore.Mvc.HttpPostAttribute), +and [`[Produces]`](xref:Microsoft.AspNetCore.Mvc.ProducesAttribute). +In minimal APIs, metadata can be collected from attributes, but may also be set by using extension methods +and other strategies, such as returning from route handlers. +The following table provides an overview of the metadata collected and the strategies for setting it. + +| Metadata | Attribute | Extension method | Other strategies | +| --- | --- | --- | +| summary | [`[EndpointSummary]`](xref:Microsoft.AspNetCore.Http.EndpointSummaryAttribute) | | | +| description | [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) | | | +| tags | [`[Tags]`](xref:Microsoft.AspNetCore.Http.TagsAttribute) | | | +| operationId | [`[EndpointName]`](xref:Microsoft.AspNetCore.Routing.EndpointNameAttribute) | | | +| parameters | [`[FromQuery]`](xref:Microsoft.AspNetCore.Mvc.FromQueryAttribute), [`[FromRoute]`](xref:Microsoft.AspNetCore.Mvc.FromRouteAttribute), [`[FromHeader]`](xref:Microsoft.AspNetCore.Mvc.FromHeaderAttribute), [`[FromForm]`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) | | +| parameter description | [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) | | | +| requestBody | [`[FromBody]`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) | | | +| responses | [`[Produces]`](xref:Microsoft.AspNetCore.Mvc.ProducesAttribute) | , | | +| Excluding endpoints | [`[ExcludeFromDescription]`](xref:Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute), [`[ApiExplorerSettings]`](xref:Microsoft.AspNetCore.Mvc.ApiExplorerSettingsAttribute) | | | + +ASP.NET Core does not collect metadata from XML doc comments. + +The following sections demonstrate how to include metadata in an app to customize the generated OpenAPI document. + +### Summary and description + +The endpoint summary and description can be set using the [`[EndpointSummary]`](xref:Microsoft.AspNetCore.Http.EndpointSummaryAttribute) and [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) attributes, +or in minimal APIs, using the and extension methods. + +#### [Minimal APIs](#tab/minimal-apis) + +The following sample demonstrates the different strategies for setting summaries and descriptions. + +Note that the attributes are placed on the delegate method and not on the app.MapGet method. + +```csharp +app.MapGet("/extension-methods", () => "Hello world!") + .WithSummary("This is a summary.") + .WithDescription("This is a description."); + +app.MapGet("/attributes", + [EndpointSummary("This is a summary.")] + [EndpointDescription("This is a description.")] + () => "Hello world!"); +``` + +#### [Controllers](#tab/controllers) + +The following sample demonstrates how to set summaries and descriptions. + +```csharp + [EndpointSummary("This is a summary.")] + [EndpointDescription("This is a description.")] + [HttpGet("attributes")] + public IResult Attributes() + { + return Results.Ok("Hello world!"); + } +``` +--- + +### tags + +OpenAPI supports specifying tags on each endpoint as a form of categorization. + +#### [Minimal APIs](#tab/minimal-apis) + +In minimal APIs, tags can be set using either the [`[Tags]`](xref:Microsoft.AspNetCore.Http.TagsAttribute) attribute or the extension method. + +The following sample demonstrates the different strategies for setting tags. + +```csharp +app.MapGet("/extension-methods", () => "Hello world!") + .WithTags("todos", "projects"); + +app.MapGet("/attributes", + [Tags("todos", "projects")] + () => "Hello world!"); +``` + +#### [Controllers](#tab/controllers) + +In controller-based apps, the controller name is automatically added as a tag on each of its endpoints, but this can be overridden using the [`[Tags]`](xref:Microsoft.AspNetCore.Http.TagsAttribute) attribute. + +The following sample demonstrates how to set tags. + +```csharp + [Tags(["todos", "projects"])] + [HttpGet("attributes")] + public IResult Attributes() + { + return Results.Ok("Hello world!"); + } +``` +--- + +### operationId + +OpenAPI supports an operationId on each endpoint as a unique identifier or name for the operation. + +#### [Minimal APIs](#tab/minimal-apis) + +In minimal APIs, the operationId can be set using either the [`[EndpointName]`](xref:Microsoft.AspNetCore.Routing.EndpointNameAttribute) attribute or the extension method. + +The following sample demonstrates the different strategies for setting the operationId. + +```csharp +app.MapGet("/extension-methods", () => "Hello world!") + .WithName("FromExtensionMethods"); + +app.MapGet("/attributes", + [EndpointName("FromAttributes")] + () => "Hello world!"); +``` + +#### [Controllers](#tab/controllers) + +In controller-based apps, the operationId can be set using the [`[EndpointName]`](xref:Microsoft.AspNetCore.Routing.EndpointNameAttribute) attribute. + +The following sample demonstrates how to set the operationId. + +```csharp + [EndpointName("FromAttributes")] + [HttpGet("attributes")] + public IResult Attributes() + { + return Results.Ok("Hello world!"); + } +``` +--- + +### parameters + +OpenAPI supports annotating path, query string, header, and cookie parameters that are consumed by an API. + +The framework infers the types for request parameters automatically based on the signature of the route handler. + +The [`[EndpointDescription]`](xref:Microsoft.AspNetCore.Http.EndpointDescriptionAttribute) attribute can be used to provide a description for a parameter. + +#### [Minimal APIs](#tab/minimal-apis) + +The follow sample demonstrates how to set a description for a parameter. + +```csharp +app.MapGet("/attributes", + ([Description("This is a description.")] string name) => "Hello world!"); +``` + +#### [Controllers](#tab/controllers) + +The following sample demonstrates how to set a description for a parameter. + +```csharp + [HttpGet("attributes")] + public IResult Attributes([Description("This is a description.")] string name) + { + return Results.Ok("Hello world!"); + } +``` +--- + +### Describe the request body + +The `requestBody` field in OpenAPI describes the body of a request that an API client can send to the server, including the content type(s) supported and the schema for the body content. + +When the endpoint handler method accepts parameters that are bound from the request body, ASP.NET Core generates a corresponding `requestBody` for the operation in the OpenAPI document. Metadata for the request body can also be specified using attributes or extension methods. Additional metadata can be set with a [document transformer](xref:fundamentals/openapi/customize-openapi#use-document-transformers) or [operation transformer](xref:fundamentals/openapi/customize-openapi#use-operation-transformers). + +If the endpoint doesn't define any parameters bound to the request body, but instead consumes the request body from the directly, ASP.NET Core provides mechanisms to specify request body metadata. This is a common scenario for endpoints that process the request body as a stream. + +Some request body metadata can be determined from the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) or [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) parameters of the route handler method. + +A description for the request body can be set with a [`[Description]`](xref:System.ComponentModel.DescriptionAttribute) attribute on the parameter with [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) or [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute). + +If the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) parameter is non-nullable and is not set to in the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) attribute, the request body is required and the `required` field of the `requestBody` is set to `true` in the generated OpenAPI document. +Form bodies are always required and have `required` set to `true`. + +Use a [document transformer](xref:fundamentals/openapi/customize-openapi#use-document-transformers) or an [operation transformer](xref:fundamentals/openapi/customize-openapi#use-operation-transformers) to set the `example`, `examples`, or `encoding` fields, or to add specification extensions for the request body in the generated OpenAPI document. + +Other mechanisms for setting request body metadata depend on the type of app being developed and are described in the following sections. + +#### [Minimal APIs](#tab/minimal-apis) + +The content types for the request body in the generated OpenAPI document are determined from the type of the parameter that is bound to the request body or specified with the extension method. +By default, the content type of a [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) parameter will be `application/json` and the content type for [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) parameter(s) will be `multipart/form-data` or `application/x-www-form-urlencoded`. + +Support for these default content types is built in to Minimal APIs, and other content types can be handled by using custom binding. +See the [Custom binding](xref:fundamentals/minimal-apis/parameter-binding#custom-binding) topic of the Minimal APIs documentation for more information. + +There are several ways to specify a different content type for the request body. +If the type of the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) parameter implements , ASP.NET Core uses this interface to determine the content type(s) in the request body. +The framework uses the method of this interface to set the content type(s) and type of the body content of the request body. For example, a `Todo` class that accepts either `application/xml` or `text/xml` content-type can use to provide this information to the framework. + +```csharp +public class Todo : IEndpointParameterMetadataProvider +{ + public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder) + { + builder.Metadata.Add(new AcceptsMetadata(["application/xml", "text/xml"], typeof(Todo))); + } +} +``` + +The extension method can also be used to specify the content type of the request body. +In the following example, the endpoint accepts a `Todo` object in the request body with an expected content-type of `application/xml`. + +```csharp +app.MapPut("/todos/{id}", (int id, Todo todo) => ...) + .Accepts("application/xml"); +``` + +Since `application/xml` is not a built-in content type, the `Todo` class must implement the interface to provide a custom binding for the request body. For example: + +```csharp +public class Todo : IBindableFromHttpContext +{ + public static async ValueTask BindAsync(HttpContext context, ParameterInfo parameter) + { + var xmlDoc = await XDocument.LoadAsync(context.Request.Body, LoadOptions.None, context.RequestAborted); + var serializer = new XmlSerializer(typeof(Todo)); + return (Todo?)serializer.Deserialize(xmlDoc.CreateReader()); + } +``` + +If the endpoint doesn't define any parameters bound to the request body, use the extension method to specify the content type that the endpoint accepts. + +If you specify multiple times, only metadata from the last one is used -- they aren't combined. + +#### [Controllers](#tab/controllers) + +In controller-based apps, the content type(s) for the request body in the generated OpenAPI document are determined from the type of the parameter that is bound to the request body, the types configured in the application, or by a [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute on the route handler method. + +ASP.NET Core uses an to deserialize a [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) request body. +InputFormatters are configured in the passed to the extension method for the app's service collection. +Each input formatter declares the content types it can handle, in its property, and the type(s) of body content it can handle, with its method. + +ASP.NET Core MVC includes built-in input formatters for JSON and XML, though only the JSON input formatter is enabled by default. +The built-in JSON input formatter supports the `application/json`, `text/json`, and `application/*+json` content types, and the built-in XML input formatter supports the `application/xml`, `text/xml`, and `application/*+xml` content types. + +By default, the content type of a [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) request body may be any content type accepted by an for the [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) parameter type. For a request body with [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) parameter(s) the default content types are `multipart/form-data` or `application/x-www-form-urlencoded`.These content types will be included in the generated OpenAPI document if the [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute is not specified on the route handler method. + +The content type(s) accepted by a route handler can be restricted using a [filter](xref:mvc/controllers/filters) on the endpoint (action scope). +The [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute adds an action scope filter to the endpoint that restricts the content types that a route handler will accept. +In this case, the requestBody in the generated OpenAPI document will include only the content type(s) specified in the [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute. + +A [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute can't add support for a content type that doesn't have an associated input formatter, and the generated OpenAPI document doesn't include any content types that don't have an associated input formatter. + +For content types other than JSON or XML, you need to create a custom input formatter. +For more information and examples, see [Custom formatters in ASP.NET Core Web API](xref:web-api/advanced/custom-formatters). + +If the route handler doesn't have a [`FromBody`](xref:Microsoft.AspNetCore.Mvc.FromBodyAttribute) or [`FromForm`](xref:Microsoft.AspNetCore.Mvc.FromFormAttribute) parameter, the route handler might read the request body directly from the `Request.Body` stream and might use the [`[Consumes]`](xref:Microsoft.AspNetCore.Mvc.ConsumesAttribute) attribute to restrict the content types allowed, but no requestBody is generated in the OpenAPI document. + +--- + +### Describe response types + +OpenAPI supports providing a description of the responses returned from an API. ASP.NET Core provides several strategies for setting the response metadata of an endpoint. Response metadata that can be set includes the status code, the type of the response body, and content type(s) of a response. Responses in OpenAPI may have additional metadata, such as description, headers, links, and examples. This additional metadata can be set with a [document transformer](xref:fundamentals/openapi/customize-openapi#use-document-transformers) or [operation transformer](xref:fundamentals/openapi/customize-openapi#use-operation-transformers). + +The specific mechanisms for setting response metadata depend on the type of app being developed. + +#### [Minimal APIs](#tab/minimal-apis) + +In Minimal API apps, ASP.NET Core can extract the response metadata added by extension methods on the endpoint, attributes on the route handler, and the return type of the route handler. + +* The extension method can be used on the endpoint to specify the status code, the type of the response body, and content type(s) of a response from an endpoint. +* The [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) or attribute can be used to specify the type of the response body. +* A route handler can be used to return a type that implements to specify the type and content-type(s) of the response body. +* The extension method on the endpoint can be used to specify the status code and content-type(s) of an error response. + +Note that the and extension methods are supported on both and on . This allows, for example, a common set of error responses to be defined for all operations in a group. + +When not specified by one of the preceding strategies, the: +* Status code for the response defaults to 200. +* Schema for the response body can be inferred from the implicit or explicit return type of the endpoint method, for example, from `T` in ; otherwise, it's considered to be unspecified. +* Content-type for the specified or inferred response body is "application/json". + +In Minimal APIs, the extension method and the [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) attribute only set the response metadata for the endpoint. They do not modify or constrain the behavior of the endpoint, which may return a different status code or response body type than specified by the metadata, and the content-type is determined by the return type of the route handler method, irrespective of any content-type specified in attributes or extension methods. + +The extension method can specify an endpoint's response type, with a default status code of 200 and a default content type of `application/json`. The following example illustrates this: + +```csharp +app.MapGet("/todos", async (TodoDb db) => await db.Todos.ToListAsync()) + .Produces>(); +``` + +The [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) can be used to add response metadata to an endpoint. Note that the attribute is applied to the route handler method, not the method invocation to create the route, as shown in the following example: + +```csharp +app.MapGet("/todos", + [ProducesResponseType>(200)] + async (TodoDb db) => await db.Todos.ToListAsync()); +``` + +Using in the implementation of an endpoint's route handler automatically includes the response type metadata for the endpoint. For example, the following code automatically annotates the endpoint with a response under the `200` status code with an `application/json` content type. + +```csharp +app.MapGet("/todos", async (TodoDb db) => +{ + var todos = await db.Todos.ToListAsync(); + return TypedResults.Ok(todos); +}); +``` + +Only return types that implement create a `responses` entry in the OpenAPI document. The following is a partial list of some of the helper methods that produce a `responses` entry: + +| TypedResults helper method | status code | +| -------------------------- | ----------- | +| Ok() | 200 | +| Created() | 201 | +| CreatedAtRoute() | 201 | +| Accepted() | 202 | +| AcceptedAtRoute() | 202 | +| NoContent() | 204 | +| BadRequest() | 400 | +| ValidationProblem() | 400 | +| NotFound() | 404 | +| Conflict() | 409 | +| UnprocessableEntity() | 422 | + +All of these methods except `NoContent` have a generic overload that specifies the type of the response body. + +A class can be implemented to set the endpoint metadata and return it from the route handler. + +##### Set responses for `ProblemDetails` + +When setting the response type for endpoints that may return a ProblemDetails response, the following can be used to add the appropriate response metadata for the endpoint: + +* +* extension method. +* with a status code in the (400-499) range. + +For more information on how to configure a Minimal API app to return ProblemDetails responses, see . + +##### Multiple response types + +If an endpoint can return different response types in different scenarios, you can provide metadata in the following ways: + +* Call the extension method multiple times, as shown in the following example: + + [!code-csharp[](~/fundamentals/minimal-apis/samples/todo/Program.cs?name=snippet_getCustom)] + +* Use in the signature and in the body of the handler, as shown in the following example: + + :::code language="csharp" source="~/../AspNetCore.Docs.Samples/fundamentals/minimal-apis/samples/MultipleResultTypes/Program.cs" id="snippet_multiple_result_types"::: + + The `Results` [union types](https://en.wikipedia.org/wiki/Union_type) declare that a route handler returns multiple `IResult`-implementing concrete types, and any of those types that implement will contribute to the endpoint’s metadata. + + The union types implement implicit cast operators. These operators enable the compiler to automatically convert the types specified in the generic arguments to an instance of the union type. This capability has the added benefit of providing compile-time checking that a route handler only returns the results that it declares it does. Attempting to return a type that isn't declared as one of the generic arguments to `Results` results in a compilation error. + +#### [Controllers](#tab/controllers) + +In controller-based apps, ASP.NET Core can extract the response metadata from the action method signature, attributes, and conventions. + +* You can use the [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) or attribute to specify the status code, the type of the response body, and content type(s) of a response from an action method. +* You can use the [`[Produces]`](xref:Microsoft.AspNetCore.Mvc.ProducesAttribute) or attribute to specify the type of the response body. +* You can use the [`[ProducesDefaultResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesDefaultResponseTypeAttribute) attribute to specify the response body type for the "default" response. +* You can use the [`[ProducesErrorResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesErrorResponseTypeAttribute) attribute to specify the response body type for an error response. However, be aware that this is only complements the status code and content type(s) specified by an [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) attribute with a 4XX status code. + +Only one [`[Produces]`](xref:Microsoft.AspNetCore.Mvc.ProducesAttribute) or attributes may be applied to an action method, but multiple [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) or attributes with different status codes may be applied to a single action method. + +All of the above attributes can be applied to individual action methods or to the controller class where it applies to all action methods in the controller. + +When not specified by an attribute: +* the status code for the response defaults to 200, +* the schema for the response body of 2xx responses may be inferred from the return type of the action method, e.g. from `T` in , but otherwise is considered to be not specified, +* the schema for the response body of 4xx responses is inferred to be a problem details object, +* the schema for the response body of 3xx and 5xx responses is considered to be not specified, +* the content-type for the response body can be inferred from the return type of the action method and the set of output formatters. + +Note that there are no compile-time checks to ensure that the response metadata specified with a [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) attribute is consistent with the actual behavior of the action method, +which may return a different status code or response body type than specified by the metadata. + +In controller-based apps, ASP.NET responds with a ProblemDetails response type when model validation fails or when the action method returns a result with a 4xx or 5xx HTTP status code. Validation errors typically use the 400 status code, so you can use the [`[ProducesResponseType]`](xref:Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute) attribute to specify the error response for an action, as shown in the following example: + +```csharp +[HttpPut("/todos/{id}")] +[ProducesResponseType(StatusCodes.Status200OK, "application/json")] +[ProducesResponseType(StatusCodes.Status201Created, "application/json")] +[ProducesResponseType(StatusCodes.Status400BadRequest, "application/problem+json")] +public async Task> CreateOrReplaceTodo(string id, Todo todo) +``` + +This example also illustrates how to define multiple response types for an action method, including the content type of the response body. + +--- + +### Exclude endpoints from the generated document + +By default, all endpoints that are defined in an app are documented in the generated OpenAPI file, but endpoints can be excluded from the document using attributes or extension methods. + +The mechanism for specifying an endpoint that should be excluded depends on the type of app being developed. + +#### [Minimal APIs](#tab/minimal-apis) + +Minimal APIs support two strategies for excluding a given endpoint from the OpenAPI document: + +* extension method +* [`[ExcludeFromDescription]`](xref:Microsoft.AspNetCore.Routing.ExcludeFromDescriptionAttribute) attribute + +The following sample demonstrates the different strategies for excluding a given endpoint from the generated OpenAPI document. + +```csharp +app.MapGet("/extension-method", () => "Hello world!") + .ExcludeFromDescription(); + +app.MapGet("/attributes", + [ExcludeFromDescription] + () => "Hello world!"); +``` + +#### [Controllers](#tab/controllers) + +In controller-based apps, the [`[ApiExplorerSettings]`](xref:Microsoft.AspNetCore.Mvc.ApiExplorerSettingsAttribute) attribute can be used to exclude an endpoint or all endpoints in a controller class from the OpenAPI document. + +The following example demonstrates how to exclude an endpoint from the generated OpenAPI document: + +```csharp + [HttpGet("/private")] + [ApiExplorerSettings(IgnoreApi = true)] + public IActionResult PrivateEndpoint() { + return Ok("This is a private endpoint"); + } +``` +--- + +## Include OpenAPI metadata for data types + +C# classes or records used in request or response bodies are represented as schemas +in the generated OpenAPI document. +By default, only public properties are represented in the schema, but there are + to also create schema properties for fields. + +When the is set to camel-case (this is the default +in ASP.NET web applications), property names in a schema are the camel-case form +of the class or record property name. +The [`[JsonPropertyName]`](xref:System.Text.Json.Serialization.JsonPropertyNameAttribute) can be used on an individual property to specify the name +of the property in the schema. + +### type and format + +The JSON Schema library maps standard C# types to OpenAPI `type` and `format` as follows: + +| C# Type | OpenAPI `type` | OpenAPI `format` | +| -------------- | -------------- | ---------------- | +| int | integer | int32 | +| long | integer | int64 | +| short | integer | int16 | +| byte | integer | uint8 | +| float | number | float | +| double | number | double | +| decimal | number | double | +| bool | boolean | | +| string | string | | +| char | string | char | +| byte[] | string | byte | +| DateTimeOffset | string | date-time | +| DateOnly | string | date | +| TimeOnly | string | time | +| Uri | string | uri | +| Guid | string | uuid | +| object | _omitted_ | | +| dynamic | _omitted_ | | + +Note that object and dynamic types have _no_ type defined in the OpenAPI because these can contain data of any type, including primitive types like int or string. + +The `type` and `format` can also be set with a [Schema Transformer](xref:fundamentals/openapi/customize-openapi#use-schema-transformers). For example, you may want the `format` of decimal types to be `decimal` instead of `double`. + +### Use attributes to add metadata + +ASP.NET uses metadata from attributes on class or record properties to set metadata on the corresponding properties of the generated schema. + +The following table summarizes attributes from the `System.ComponentModel` namespace that provide metadata for the generated schema: + +| Attribute | Description | +| ---------------------------- | ----------- | +| [`[Description]`](xref:System.ComponentModel.DescriptionAttribute) | Sets the `description` of a property in the schema. | +| [`[Required]`](xref:System.ComponentModel.DataAnnotations.RequiredAttribute) | Marks a property as `required` in the schema. | +| [`[DefaultValue]`](xref:System.ComponentModel.DefaultValueAttribute) | Sets the `default` value of a property in the schema. | +| [`[Range]`](xref:System.ComponentModel.DataAnnotations.RangeAttribute) | Sets the `minimum` and `maximum` value of an integer or number. | +| [`[MinLength]`](xref:System.ComponentModel.DataAnnotations.MinLengthAttribute) | Sets the `minLength` of a string. | +| [`[MaxLength]`](xref:System.ComponentModel.DataAnnotations.MaxLengthAttribute) | Sets the `maxLength` of a string. | +| [`[RegularExpression]`](xref:System.ComponentModel.DataAnnotations.RegularExpressionAttribute) | Sets the `pattern` of a string. | + +Note that in controller-based apps, these attributes add filters to the operation to validate that any incoming data satisfies the constraints. In Minimal APIs, these attributes set the metadata in the generated schema but validation must be performed explicitly via an endpoint filter, in the route handler's logic, or via a third-party package. + +### Other sources of metadata for generated schemas + +#### required + +Properties can also be marked as `required` with the [required](/dotnet/csharp/language-reference/proposals/csharp-11.0/required-members#required-modifier) modifier. + +#### enum + +Enum types in C# are integer-based, but can be represented as strings in JSON with a [`[JsonConverter]`](xref:System.Text.Json.Serialization.JsonConverterAttribute) and a . When an enum type is represented as a string in JSON, the generated schema will have an `enum` property with the string values of the enum. + +The following example demonstrates how to use a `JsonStringEnumConverter` to represent an enum as a string in JSON: + +```csharp +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum DayOfTheWeekAsString +{ + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday +} +``` + +A special case is when an enum type has the [Flags] attribute, which indicates that the enum can be treated as a bit field; that is, a set of flags. A flags enum with a [JsonConverterAttribute] will be defined as `type: string` in the generated schema with no `enum` property, since the value could be any combination of the enum values. For example, the following enum: + +```csharp +[Flags, JsonConverter(typeof(JsonStringEnumConverter))] +public enum PizzaToppings { Pepperoni = 1, Sausage = 2, Mushrooms = 4, Anchovies = 8 } +``` + +could have values such as `"Pepperoni, Sausage"` or `"Sausage, Mushrooms, Anchovies"`. + +An enum type without a [`[JsonConverter]`](xref:System.Text.Json.Serialization.JsonConverterAttribute) will be defined as `type: integer` in the generated schema. + +**Note:** The [`[AllowedValues]`](xref:System.ComponentModel.DataAnnotations.AllowedValuesAttribute) attribute does not set the `enum` values of a property. + +#### nullable + +Properties defined as a nullable value or reference type have `nullable: true` in the generated schema. This is consistent with the default behavior of the deserializer, which accepts `null` as a valid value for a nullable property. + +#### additionalProperties + +Schemas are generated without an `additionalProperties` assertion by default, which implies the default of `true`. This is consistent with the default behavior of the deserializer, which silently ignores additional properties in a JSON object. + +If the additional properties of a schema should only have values of a specific type, define the property or class as a `Dictionary`. The key type for the dictionary must be `string`. This generates a schema with `additionalProperties` specifying the schema for "type" as the required value types. + +#### Polymorphic types + +Use the [`[JsonPolymorphic]`](xref:System.Text.Json.Serialization.JsonPolymorphicAttribute) and [`[JsonDerivedType]`](xref:System.Text.Json.Serialization.JsonDerivedTypeAttribute) attributes on a parent class to to specify the discriminator field and subtypes for a polymorphic type. + +The [`[JsonDerivedType]`](xref:System.Text.Json.Serialization.JsonDerivedTypeAttribute) adds the discriminator field to the schema for each subclass, with an enum specifying the specific discriminator value for the subclass. This attribute also modifies the constructor of each derived class to set the discriminator value. + +An abstract class with a [`[JsonPolymorphic]`](xref:System.Text.Json.Serialization.JsonPolymorphicAttribute) attribute has a `discriminator` field in the schema, but a concrete class with a [`[JsonPolymorphic]`](xref:System.Text.Json.Serialization.JsonPolymorphicAttribute) attribute doesn't have a `discriminator` field. OpenAPI requires that the discriminator property be a required property in the schema, but since the discriminator property isn't defined in the concrete base class, the schema cannot include a `discriminator` field. + +### Add metadata with a schema transformer + +A schema transformer can be used to override any default metadata or add additional metadata, such as `example` values, to the generated schema. See [Use schema transformers](xref:fundamentals/openapi/customize-openapi#use-schema-transformers) for more information. + +## Additional resources + +* +* [OpenAPI specification](https://spec.openapis.org/oas/v3.0.3) diff --git a/aspnetcore/performance/caching/distributed/samples/8.x/RedisCache/appsettings.json b/aspnetcore/performance/caching/distributed/samples/8.x/RedisCache/appsettings.json index 808f7455d292..5970dac89bbc 100644 --- a/aspnetcore/performance/caching/distributed/samples/8.x/RedisCache/appsettings.json +++ b/aspnetcore/performance/caching/distributed/samples/8.x/RedisCache/appsettings.json @@ -7,6 +7,6 @@ }, "AllowedHosts": "*", "ConnectionStrings": { - "MyAzureRedisConStr": ".redis.cache.windows.net,abortConnect=false,ssl=true,allowAdmin=true,password=" + "MyAzureRedisConStr": "" } } diff --git a/aspnetcore/security/app-secrets.md b/aspnetcore/security/app-secrets.md index b867db7ac60f..7850e725a039 100644 --- a/aspnetcore/security/app-secrets.md +++ b/aspnetcore/security/app-secrets.md @@ -5,12 +5,13 @@ description: Learn how to store and retrieve sensitive information during the de ms.author: tdykstra monikerRange: '>= aspnetcore-3.0' ms.custom: mvc -ms.date: 10/29/2024 +ms.date: 10/30/2024 uid: security/app-secrets --- # Safe storage of app secrets in development in ASP.NET Core + [!INCLUDE[](~/includes/not-latest-version.md)] :::moniker range=">= aspnetcore-6.0" @@ -19,7 +20,7 @@ By [Rick Anderson](https://twitter.com/RickAndMSFT) and [Kirk Larkin](https://tw [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/security/app-secrets/samples) ([how to download](xref:index#how-to-download-a-sample)) -This article explains how to manage sensitive data for an ASP.NET Core app on a development machine. Never store passwords or other sensitive data in source code or configuration files. Production secrets shouldn't be used for development or test. Secrets shouldn't be deployed with the app. Production secrets should be accessed through a controlled means like Azure Key Vault. Azure test and production secrets can be stored and protected with the [Azure Key Vault configuration provider](xref:security/key-vault-configuration). +This article explains how to manage sensitive data for an ASP.NET Core app on a development machine. Never store passwords or other sensitive data in source code or configuration files. Production secrets shouldn't be used for development or test. Secrets shouldn't be deployed with the app. Production secrets should be accessed through a controlled means like Azure Key Vault. Azure test and production secrets can be stored and protected with the [Azure Key Vault configuration provider](xref:security/key-vault-configuration). For more information on authentication for deployed test and production apps, see [Secure authentication flows](xref:security/index#secure-authentication-flows). @@ -201,21 +202,15 @@ The `Movies:ConnectionString` and `Movies:ServiceApiKey` secrets are mapped to t ## String replacement with secrets -Storing passwords in plain text is insecure. For example, a database connection string stored in `appsettings.json` may include a password for the specified user: - -[!code-json[](~/security/app-secrets/samples/3.x/UserSecrets/appsettings-unsecure.json?highlight=3)] +Storing passwords in plain text is insecure. Never store secrets in a configuration file such as `appsettings.json`, which might get checked in to a source code repository. -A more secure approach is to store the password as a secret. For example: +For example, a database connection string stored in `appsettings.json` should not include a password. Instead, store the password as a secret, and include the password in the connection string at runtime. For example: ```dotnetcli -dotnet user-secrets set "DbPassword" "pass123" +dotnet user-secrets set "DbPassword" "``" ``` -Remove the `Password` key-value pair from the connection string in `appsettings.json`. For example: - -[!code-json[](~/security/app-secrets/samples/3.x/UserSecrets/appsettings.json?highlight=3)] - -The secret's value can be set on a object's property to complete the connection string: +Replace the `` placeholder in the preceding example with the password value. Set the secret's value on a object's property to include it as the password value in the connection string: [!code-csharp[](~/security/app-secrets/samples/6.x/UserSecrets/Program.cs?name=snippet_sql&highlight=5-8)] diff --git a/aspnetcore/security/app-secrets/includes/app-secrets-3-5.md b/aspnetcore/security/app-secrets/includes/app-secrets-3-5.md index 2f6e57111814..d77e8fa84075 100644 --- a/aspnetcore/security/app-secrets/includes/app-secrets-3-5.md +++ b/aspnetcore/security/app-secrets/includes/app-secrets-3-5.md @@ -8,6 +8,7 @@ By [Rick Anderson](https://twitter.com/RickAndMSFT), [Kirk Larkin](https://twitt This article explains how to manage sensitive data for an ASP.NET Core app on a development machine. Never store passwords or other sensitive data in source code or configuration files. Production secrets shouldn't be used for development or test. Secrets shouldn't be deployed with the app. Production secrets should be accessed through a controlled means like Azure Key Vault. Azure test and production secrets can be stored and protected with the [Azure Key Vault configuration provider](xref:security/key-vault-configuration). For more information on authentication for test and production environments, see [Secure authentication flows](xref:security/index#secure-authentication-flows). + ## Environment variables Environment variables are used to avoid storage of app secrets in code or in local configuration files. Environment variables override configuration values for all previously specified configuration sources. @@ -172,23 +173,17 @@ The `Movies:ConnectionString` and `Movies:ServiceApiKey` secrets are mapped to t ## String replacement with secrets -Storing passwords in plain text is insecure. For example, a database connection string stored in `appsettings.json` may include a password for the specified user: - -[!code-json[](~/security/app-secrets/samples/3.x/UserSecrets/appsettings-unsecure.json?highlight=3)] +Storing passwords in plain text is insecure. Never store secrets in a configuration file such as `appsettings.json`, which might get checked in to a source code repository. -A more secure approach is to store the password as a secret. For example: +For example, a database connection string stored in `appsettings.json` should not include a password. Instead, store the password as a secret, and include the password in the connection string at runtime. For example: ```dotnetcli -dotnet user-secrets set "DbPassword" "pass123" +dotnet user-secrets set "DbPassword" "" ``` -Remove the `Password` key-value pair from the connection string in `appsettings.json`. For example: - -[!code-json[](~/security/app-secrets/samples/3.x/UserSecrets/appsettings.json?highlight=3)] - -The secret's value can be set on a object's property to complete the connection string: +Replace the `` placeholder in the preceding example with the password value. Set the secret's value on a object's property to include it as the password value in the connection string: -[!code-csharp[](~/security/app-secrets/samples/3.x/UserSecrets/Startup2.cs?name=snippet_StartupClass&highlight=14-17)] +[!code-csharp[](~/security/app-secrets/samples/6.x/UserSecrets/Program.cs?name=snippet_sql&highlight=5-8)] ## List the secrets diff --git a/aspnetcore/security/app-secrets/samples/2.x/UserSecrets/appsettings-unsecure.json b/aspnetcore/security/app-secrets/samples/2.x/UserSecrets/appsettings-unsecure.json deleted file mode 100644 index 07ff807bf643..000000000000 --- a/aspnetcore/security/app-secrets/samples/2.x/UserSecrets/appsettings-unsecure.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "Movies": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;User Id=johndoe; $CREDENTIAL_PLACEHOLDER$;MultipleActiveResultSets=true" - } -} \ No newline at end of file diff --git a/aspnetcore/security/app-secrets/samples/3.x/UserSecrets/appsettings-unsecure.json b/aspnetcore/security/app-secrets/samples/3.x/UserSecrets/appsettings-unsecure.json deleted file mode 100644 index 14a8ebb8c3a3..000000000000 --- a/aspnetcore/security/app-secrets/samples/3.x/UserSecrets/appsettings-unsecure.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "Movies": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;User Id=johndoe;Password=pass123;MultipleActiveResultSets=true" - } -} \ No newline at end of file diff --git a/aspnetcore/security/app-secrets/samples/6.x/UserSecrets/appsettings.Development.json b/aspnetcore/security/app-secrets/samples/6.x/UserSecrets/appsettings.Development.json index e120ed2453ea..4b4bb07c20e7 100644 --- a/aspnetcore/security/app-secrets/samples/6.x/UserSecrets/appsettings.Development.json +++ b/aspnetcore/security/app-secrets/samples/6.x/UserSecrets/appsettings.Development.json @@ -8,6 +8,5 @@ }, "ConnectionStrings": { "Movies": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;User Id=johndoe;MultipleActiveResultSets=true" - }, - "DbPassword": "MySecret" + } } diff --git a/aspnetcore/security/authentication/mfa/includes/mfa-5-8.md b/aspnetcore/security/authentication/mfa/includes/mfa-5-8.md index 11b35bb1650d..1c743627c83c 100644 --- a/aspnetcore/security/authentication/mfa/includes/mfa-5-8.md +++ b/aspnetcore/security/authentication/mfa/includes/mfa-5-8.md @@ -1,4 +1,5 @@ :::moniker range=">= aspnetcore-6.0 <= aspnetcore-8.0" + By [Damien Bowden](https://github.com/damienbod) diff --git a/aspnetcore/security/data-protection/configuration/overview.md b/aspnetcore/security/data-protection/configuration/overview.md index 9c2240f3dca1..cd399865c2ff 100644 --- a/aspnetcore/security/data-protection/configuration/overview.md +++ b/aspnetcore/security/data-protection/configuration/overview.md @@ -5,10 +5,11 @@ description: Learn how to configure Data Protection in ASP.NET Core. monikerRange: '>= aspnetcore-3.1' ms.author: tdykstra ms.custom: mvc -ms.date: 6/14/2023 +ms.date: 10/30/2024 uid: security/data-protection/configuration/overview --- # Configure ASP.NET Core Data Protection + :::moniker range=">= aspnetcore-6.0" @@ -317,6 +318,8 @@ services.AddDataProtection() .ProtectKeysWithAzureKeyVault(new Uri(""), new DefaultAzureCredential()); ``` +[!INCLUDE [managed-identities](~/includes/managed-identities-conn-strings.md)] + ## PersistKeysToFileSystem To store keys on a UNC share instead of at the *%LOCALAPPDATA%* default location, configure the system with : diff --git a/aspnetcore/security/index.md b/aspnetcore/security/index.md index d8ab5f4c0068..e6d45316ce57 100644 --- a/aspnetcore/security/index.md +++ b/aspnetcore/security/index.md @@ -63,6 +63,12 @@ Managed identities are a secure way to authenticate to services without needing When the app is deployed to a test server, an environment variable can be used to set the connection string to a test database server. For more information, see [Configuration](xref:fundamentals/configuration/index). Environment variables are generally stored in plain, unencrypted text. If the machine or process is compromised, environment variables can be accessed by untrusted parties. We recommend environment variables not be used to store a production connection string as it's not the most secure approach. +Configuration data guidelines: + +* Never store passwords or other sensitive data in configuration provider code or in plain text configuration files. The [Secret Manager](xref:security/app-secrets) tool can be used to store secrets in development. +* Don't use production secrets in development or test environments. +* Specify secrets outside of the project so that they can't be accidentally committed to a source code repository. + For more information, see: * [Managed identity best practice recommendations](/entra/identity/managed-identities-azure-resources/managed-identity-best-practice-recommendations) diff --git a/aspnetcore/signalr/redis-backplane.md b/aspnetcore/signalr/redis-backplane.md index 9eb29121f152..9ca6dde89ff0 100644 --- a/aspnetcore/signalr/redis-backplane.md +++ b/aspnetcore/signalr/redis-backplane.md @@ -5,10 +5,10 @@ description: Learn how to set up a Redis backplane to enable scale-out for an AS monikerRange: '>= aspnetcore-2.1' ms.author: wpickett ms.custom: mvc -ms.date: 02/06/2024 +ms.date: 10/31/2024 uid: signalr/redis-backplane --- - + # Set up a Redis backplane for ASP.NET Core SignalR scale-out By [Andrew Stanton-Nurse](https://twitter.com/anurse), [Brady Gaster](https://twitter.com/bradygaster), and [Tom Dykstra](https://github.com/tdykstra). @@ -17,6 +17,8 @@ By [Andrew Stanton-Nurse](https://twitter.com/anurse), [Brady Gaster](https://tw This article explains SignalR-specific aspects of setting up a [Redis](https://redis.io/) server to use for scaling out an ASP.NET Core SignalR app. +[!INCLUDE [managed-identities](~/includes/managed-identities-conn-strings.md)] + ## Set up a Redis backplane * Deploy a Redis server. diff --git a/aspnetcore/signalr/redis-backplane/includes/redis-backplane.md b/aspnetcore/signalr/redis-backplane/includes/redis-backplane.md index 5807ad5dc0e3..dfe8a85f0dc9 100644 --- a/aspnetcore/signalr/redis-backplane/includes/redis-backplane.md +++ b/aspnetcore/signalr/redis-backplane/includes/redis-backplane.md @@ -1,7 +1,10 @@ :::moniker range="> aspnetcore-2.0 <= aspnetcore-5.0" + This article explains SignalR-specific aspects of setting up a [Redis](https://redis.io/) server to use for scaling out an ASP.NET Core SignalR app. +[!INCLUDE [managed-identities](~/includes/managed-identities-conn-strings.md)] + ## Set up a Redis backplane * Deploy a Redis server. diff --git a/aspnetcore/signalr/redis-backplane/includes/redis-backplane2.1.md b/aspnetcore/signalr/redis-backplane/includes/redis-backplane2.1.md index 8b7a60f036cc..2f6eafc2143a 100644 --- a/aspnetcore/signalr/redis-backplane/includes/redis-backplane2.1.md +++ b/aspnetcore/signalr/redis-backplane/includes/redis-backplane2.1.md @@ -1,7 +1,10 @@ :::moniker range="= aspnetcore-2.1" + This article explains SignalR-specific aspects of setting up a [Redis](https://redis.io/) server to use for scaling out an ASP.NET Core SignalR app. +[!INCLUDE [managed-identities](~/includes/managed-identities-conn-strings.md)] + ## Set up a Redis backplane * Deploy a Redis server. diff --git a/aspnetcore/signalr/redis-backplane/includes/redis-backplane2.2.md b/aspnetcore/signalr/redis-backplane/includes/redis-backplane2.2.md index 217cff3bec65..a3f7e923e584 100644 --- a/aspnetcore/signalr/redis-backplane/includes/redis-backplane2.2.md +++ b/aspnetcore/signalr/redis-backplane/includes/redis-backplane2.2.md @@ -1,7 +1,10 @@ :::moniker range="= aspnetcore-2.2" + This article explains SignalR-specific aspects of setting up a [Redis](https://redis.io/) server to use for scaling out an ASP.NET Core SignalR app. +[!INCLUDE [managed-identities](~/includes/managed-identities-conn-strings.md)] + ## Set up a Redis backplane * Deploy a Redis server. diff --git a/aspnetcore/signalr/redis-backplane/includes/redis-backplane3.md b/aspnetcore/signalr/redis-backplane/includes/redis-backplane3.md index 6f05ef143f7f..9cfc7f16cd0a 100644 --- a/aspnetcore/signalr/redis-backplane/includes/redis-backplane3.md +++ b/aspnetcore/signalr/redis-backplane/includes/redis-backplane3.md @@ -1,7 +1,9 @@ :::moniker range="> aspnetcore-2.2 < aspnetcore-5.0" - + This article explains SignalR-specific aspects of setting up a [Redis](https://redis.io/) server to use for scaling out an ASP.NET Core SignalR app. +[!INCLUDE [managed-identities](~/includes/managed-identities-conn-strings.md)] + ## Set up a Redis backplane * Deploy a Redis server. diff --git a/aspnetcore/signalr/redis-backplane/includes/redis-backplane5.md b/aspnetcore/signalr/redis-backplane/includes/redis-backplane5.md index a6f911ecf867..e7a3c21147b1 100644 --- a/aspnetcore/signalr/redis-backplane/includes/redis-backplane5.md +++ b/aspnetcore/signalr/redis-backplane/includes/redis-backplane5.md @@ -1,7 +1,9 @@ :::moniker range="= aspnetcore-5.0" - + This article explains SignalR-specific aspects of setting up a [Redis](https://redis.io/) server to use for scaling out an ASP.NET Core SignalR app. +[!INCLUDE [managed-identities](~/includes/managed-identities-conn-strings.md)] + ## Set up a Redis backplane * Deploy a Redis server. diff --git a/aspnetcore/signalr/redis-backplane/includes/redis-backplane6.md b/aspnetcore/signalr/redis-backplane/includes/redis-backplane6.md index a691615fc152..94be7a6b1e27 100644 --- a/aspnetcore/signalr/redis-backplane/includes/redis-backplane6.md +++ b/aspnetcore/signalr/redis-backplane/includes/redis-backplane6.md @@ -1,7 +1,9 @@ :::moniker range="= aspnetcore-6.0" - + This article explains SignalR-specific aspects of setting up a [Redis](https://redis.io/) server to use for scaling out an ASP.NET Core SignalR app. +[!INCLUDE [managed-identities](~/includes/managed-identities-conn-strings.md)] + ## Set up a Redis backplane * Deploy a Redis server. diff --git a/aspnetcore/signalr/redis-backplane/includes/redis-backplane7.md b/aspnetcore/signalr/redis-backplane/includes/redis-backplane7.md index 401bc35121e6..e777faec4b7f 100644 --- a/aspnetcore/signalr/redis-backplane/includes/redis-backplane7.md +++ b/aspnetcore/signalr/redis-backplane/includes/redis-backplane7.md @@ -1,7 +1,10 @@ :::moniker range="= aspnetcore-7.0" + This article explains SignalR-specific aspects of setting up a [Redis](https://redis.io/) server to use for scaling out an ASP.NET Core SignalR app. +[!INCLUDE [managed-identities](~/includes/managed-identities-conn-strings.md)] + ## Set up a Redis backplane * Deploy a Redis server. diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index 1a91d289a50e..5fbd7dd81c1b 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -876,10 +876,12 @@ items: uid: fundamentals/openapi/overview - name: Generate OpenAPI documents uid: fundamentals/openapi/aspnetcore-openapi + - name: Include OpenAPI metadata + uid: fundamentals/openapi/include-metadata + - name: Customize OpenAPI documents + uid: fundamentals/openapi/customize-openapi - name: Use OpenAPI documents uid: fundamentals/openapi/using-openapi-documents - - name: Generate OpenAPI documents at build-time - uid: fundamentals/openapi/buildtime-openapi - name: OpenAPI tools uid: fundamentals/openapi/openapi-tools - name: Swagger / NSwag