You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
See #66408. This proposal serves as an alternative.
In short, today, OpenAPI requires document names to be known at the time when the services are registered. There can be scenarios (e.g, Asp.Versioning) when the document names are not known that early.
Proposed API
Initial proposed API that went through API review
Add IAdditionalOpenApiDocumentNameProvider:
+ namespace Microsoft.AspNetCore.OpenApi.Extensions;++ /// <summary>+ /// An interface that lets users provide additional OpenAPI document names.+ /// </summary>+ public interface IAdditionalOpenApiDocumentNameProvider+ {+ /// <summary>+ /// Gets the additional document names.+ /// </summary>+ public IEnumerable<string> DocumentNames { get; }+ }
Make documentName in AddOpenApi nullable.
namespace Microsoft.Extensions.DependencyInjection;
public static class OpenApiServiceCollectionExtensions
{
// Alternatively, add new overload named AddOpenApiCore.
- public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName)+ public static IServiceCollection AddOpenApi(this IServiceCollection services, string? documentName)- public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName, Action<OpenApiOptions> configureOptions)+ public static IServiceCollection AddOpenApi(this IServiceCollection services, string? documentName, Action<OpenApiOptions> configureOptions)
}
Exact changes:
All keyed OpenAPI services will be registered using KeyedService.AnyKey. This allows document names that are not known during the time of building services to still work as expected.
If AddOpenApi is called with null document name, we won't register NamedService<OpenApiDocumentService> (the whole purpose of this service is to get the registered document names later - at this point, we don't even have any document name)
If AddOpenApi is called with a non-null document name, we register NamedService<OpenApiDocumentService>.
An internal implementation of IConfigureNamedOptions<OpenApiOptions> is going to be used to set the DocumentName when requesting OpenApiOptions for a specific document name.
Whenever we want to know the valid document names, we combine that information from two sources:
The new interface, if registered.
The internal NamedService<OpenApiDocumentService> which is used when AddOpenApi is given a non-null document name.
Up-to-date API proposal after API review outcome
Add IAdditionalOpenApiDocumentNameResolver:
+ namespace Microsoft.AspNetCore.OpenApi;++ /// <summary>+ /// An interface that lets users provide additional OpenAPI document names.+ /// </summary>+ public interface IAdditionalOpenApiDocumentNameResolver+ {+ /// <summary>+ /// Gets the additional document names.+ /// </summary>+ IEnumerable<string> ResolveDocumentNames();+ }
Introduce AddOpenApiCore.
namespace Microsoft.Extensions.DependencyInjection;
public static class OpenApiServiceCollectionExtensions
{
+ public static IServiceCollection AddOpenApiCore(this IServiceCollection services, Action<OpenApiOptions> configureOptions)
}
Exact changes:
All keyed OpenAPI services will be registered using KeyedService.AnyKey. This allows document names that are not known during the time of building services to still work as expected.
When AddOpenApiCore is called, we won't register NamedService<OpenApiDocumentService> (the whole purpose of this service is to get the registered document names later - at this point, we don't even have any document name)
When AddOpenApi is called, we register NamedService<OpenApiDocumentService>.
An internal implementation of IConfigureNamedOptions<OpenApiOptions> is going to be used to set the DocumentName when requesting OpenApiOptions for a specific document name.
Whenever we want to know the valid document names, we combine that information from two sources:
The new interface, if registered.
The internal NamedService<OpenApiDocumentService> which is used when AddOpenApi is used.
Then OpenApiVersionedDocumentNamesProvider is responsible for providing the additional document names.
Alternative Designs
We should consider if we should take this a step further. As currently proposed and implemented in prototype, the public IAdditionalOpenApiDocumentNameProvider only really provides the additional document names, as the name suggests.
We still have an internal IDocumentProvider interface. One of its responsibilities is to provide the document names, and its implementation is updated to "combine" all the document names (ones added explicitly through AddOpenApi and the additional ones provided externally via the new interface)
What we can consider is, if the public interface should simply be responsible of providing all the document names. This might also help us clean all the reflection logic for build-time generation. If we want to do so, we need to have a concrete API shape for this.
Important
In #58353, it mentions that the IDocumentProvider interface "should be fleshed out and made public"
As might potentially need to tackle that as part of this proposal.
Important
Visual Studio also does reflection over IDocumentProvider and changes to that interface might be breaking for VS. We could say it's acceptable and that Visual Studio has to patch their implementation. But it's a risk worth clarifying.
Background and Motivation
See #66408. This proposal serves as an alternative.
In short, today, OpenAPI requires document names to be known at the time when the services are registered. There can be scenarios (e.g, Asp.Versioning) when the document names are not known that early.
Proposed API
Initial proposed API that went through API review
Add
IAdditionalOpenApiDocumentNameProvider:Make documentName in AddOpenApi nullable.
namespace Microsoft.Extensions.DependencyInjection; public static class OpenApiServiceCollectionExtensions { // Alternatively, add new overload named AddOpenApiCore. - public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName) + public static IServiceCollection AddOpenApi(this IServiceCollection services, string? documentName) - public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName, Action<OpenApiOptions> configureOptions) + public static IServiceCollection AddOpenApi(this IServiceCollection services, string? documentName, Action<OpenApiOptions> configureOptions) }Exact changes:
KeyedService.AnyKey. This allows document names that are not known during the time of building services to still work as expected.AddOpenApiis called with null document name, we won't registerNamedService<OpenApiDocumentService>(the whole purpose of this service is to get the registered document names later - at this point, we don't even have any document name)AddOpenApiis called with a non-null document name, we registerNamedService<OpenApiDocumentService>.IConfigureNamedOptions<OpenApiOptions>is going to be used to set the DocumentName when requesting OpenApiOptions for a specific document name.NamedService<OpenApiDocumentService>which is used when AddOpenApi is given a non-null document name.Up-to-date API proposal after API review outcome
Add
IAdditionalOpenApiDocumentNameResolver:Introduce AddOpenApiCore.
namespace Microsoft.Extensions.DependencyInjection; public static class OpenApiServiceCollectionExtensions { + public static IServiceCollection AddOpenApiCore(this IServiceCollection services, Action<OpenApiOptions> configureOptions) }Exact changes:
KeyedService.AnyKey. This allows document names that are not known during the time of building services to still work as expected.AddOpenApiCoreis called, we won't registerNamedService<OpenApiDocumentService>(the whole purpose of this service is to get the registered document names later - at this point, we don't even have any document name)AddOpenApiis called, we registerNamedService<OpenApiDocumentService>.IConfigureNamedOptions<OpenApiOptions>is going to be used to set the DocumentName when requesting OpenApiOptions for a specific document name.NamedService<OpenApiDocumentService>which is used when AddOpenApi is used.Usage Examples
See current prototype in dotnet/aspnet-api-versioning#1186 (dotnet/aspnet-api-versioning@5005039). In short:
Then
OpenApiVersionedDocumentNamesProvideris responsible for providing the additional document names.Alternative Designs
We should consider if we should take this a step further. As currently proposed and implemented in prototype, the public
IAdditionalOpenApiDocumentNameProvideronly really provides the additional document names, as the name suggests.We still have an internal
IDocumentProviderinterface. One of its responsibilities is to provide the document names, and its implementation is updated to "combine" all the document names (ones added explicitly through AddOpenApi and the additional ones provided externally via the new interface)What we can consider is, if the public interface should simply be responsible of providing all the document names. This might also help us clean all the reflection logic for build-time generation. If we want to do so, we need to have a concrete API shape for this.
Important
In #58353, it mentions that the IDocumentProvider interface "should be fleshed out and made public"
As might potentially need to tackle that as part of this proposal.
Important
Visual Studio also does reflection over IDocumentProvider and changes to that interface might be breaking for VS. We could say it's acceptable and that Visual Studio has to patch their implementation. But it's a risk worth clarifying.
Risks