Skip to content

API proposal: Allow OpenAPI registration to resolve document names upon request #66725

Description

@Youssef1313

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
  1. 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; }
    + }
  2. 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
  1. 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();
    + }
  2. 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.

Usage Examples

See current prototype in dotnet/aspnet-api-versioning#1186 (dotnet/aspnet-api-versioning@5005039). In short:

services.AddSingleton<IAdditionalOpenApiDocumentNameProvider, OpenApiVersionedDocumentNamesProvider>();
services.AddOpenApiCore();

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.

Risks

Metadata

Metadata

Labels

api-approvedAPI was approved in API review, it can be implementedarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcfeature-openapi

Fields

No fields configured for Feature.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions