Description
When using minimal APIs with AddApiVersioning().AddApiExplorer().AddOpenApi(), the generated OpenAPI documents (e.g. /openapi/v1.json) have an empty paths object and no schemas, even though the document itself is otherwise valid (correct info.version, versioning metadata, etc.).
The same configuration with controllers works correctly — OpenAPI documents are fully populated with paths and schemas.
Steps to Reproduce
- Run the
MinimalOpenApiExample sample project
- Navigate to
/openapi/v1.json
- Observe empty
paths: {} and no schemas
Expected: paths and schemas for Orders and People endpoints
Actual: empty paths, no schemas
For comparison, run the OpenApiExample (controllers) project — its /openapi/v1.json has fully populated paths and schemas.
Root Cause
AddApiExplorerServices() in Asp.Versioning.Mvc.ApiExplorer registers AddMvcCore().AddApiExplorer(), which only registers DefaultApiDescriptionProvider — the provider that discovers controller actions via IActionDescriptorCollectionProvider.
For minimal APIs, the built-in ASP.NET Core EndpointMetadataApiDescriptionProvider (registered by AddEndpointsApiExplorer()) is responsible for discovering minimal API endpoints via EndpointDataSource. This provider is never registered by the API versioning library.
The downstream VersionedApiDescriptionProvider already supports minimal API descriptions (it has TryUpdateControllerRouteValueForMinimalApi() and correctly handles non-ControllerActionDescriptor actions), but it never receives any minimal API descriptions because they're never generated upstream.
The OpenApiDocumentService (Microsoft's internal class) consumes IApiDescriptionGroupCollectionProvider which aggregates all IApiDescriptionProvider results. Since EndpointMetadataApiDescriptionProvider is missing, minimal API endpoints are invisible.
Proposed Fix
Add services.AddEndpointsApiExplorer() in AddApiExplorerServices() alongside the existing services.AddMvcCore().AddApiExplorer():
private static void AddApiExplorerServices( IApiVersioningBuilder builder )
{
builder.AddMvc();
var services = builder.Services;
services.AddMvcCore().AddApiExplorer();
services.AddEndpointsApiExplorer(); // <-- register minimal API endpoint discovery
// ...
}
AddEndpointsApiExplorer() is in the shared framework (Microsoft.AspNetCore.Routing) — no new package reference needed
- Uses
TryAddEnumerable internally — safe to call multiple times
EndpointMetadataApiDescriptionProvider runs at Order = -1100, before VersionedApiDescriptionProvider (Order = 0) — correct sequence
- For controller-only apps, it's a no-op (produces no descriptions for controller endpoints)
Related: Test minimal_api_should_generate_expected_open_api_document
The existing test minimal_api_should_generate_expected_open_api_document in AcceptanceTest.cs appears to validate minimal API OpenAPI generation, but it actually registers both controllers (AddControllers().AddApplicationPart(...)) and minimal API endpoints. The expected fixture v1.json only contains the controller path /Test — the minimal API path /test/{id} is silently absent. The test passes because it validates against controller output, masking this bug.
This test should be restructured to:
- Remove controller registration so it tests only minimal API endpoint discovery
- Use a dedicated fixture with the expected minimal API paths
- A separate mixed test could verify both controllers and minimal APIs appear together
Description
When using minimal APIs with
AddApiVersioning().AddApiExplorer().AddOpenApi(), the generated OpenAPI documents (e.g./openapi/v1.json) have an emptypathsobject and no schemas, even though the document itself is otherwise valid (correctinfo.version, versioning metadata, etc.).The same configuration with controllers works correctly — OpenAPI documents are fully populated with paths and schemas.
Steps to Reproduce
MinimalOpenApiExamplesample project/openapi/v1.jsonpaths: {}and no schemasExpected: paths and schemas for Orders and People endpoints
Actual: empty paths, no schemas
For comparison, run the
OpenApiExample(controllers) project — its/openapi/v1.jsonhas fully populated paths and schemas.Root Cause
AddApiExplorerServices()inAsp.Versioning.Mvc.ApiExplorerregistersAddMvcCore().AddApiExplorer(), which only registersDefaultApiDescriptionProvider— the provider that discovers controller actions viaIActionDescriptorCollectionProvider.For minimal APIs, the built-in ASP.NET Core
EndpointMetadataApiDescriptionProvider(registered byAddEndpointsApiExplorer()) is responsible for discovering minimal API endpoints viaEndpointDataSource. This provider is never registered by the API versioning library.The downstream
VersionedApiDescriptionProvideralready supports minimal API descriptions (it hasTryUpdateControllerRouteValueForMinimalApi()and correctly handles non-ControllerActionDescriptoractions), but it never receives any minimal API descriptions because they're never generated upstream.The
OpenApiDocumentService(Microsoft's internal class) consumesIApiDescriptionGroupCollectionProviderwhich aggregates allIApiDescriptionProviderresults. SinceEndpointMetadataApiDescriptionProvideris missing, minimal API endpoints are invisible.Proposed Fix
Add
services.AddEndpointsApiExplorer()inAddApiExplorerServices()alongside the existingservices.AddMvcCore().AddApiExplorer():AddEndpointsApiExplorer()is in the shared framework (Microsoft.AspNetCore.Routing) — no new package reference neededTryAddEnumerableinternally — safe to call multiple timesEndpointMetadataApiDescriptionProviderruns atOrder = -1100, beforeVersionedApiDescriptionProvider(Order = 0) — correct sequenceRelated: Test
minimal_api_should_generate_expected_open_api_documentThe existing test
minimal_api_should_generate_expected_open_api_documentinAcceptanceTest.csappears to validate minimal API OpenAPI generation, but it actually registers both controllers (AddControllers().AddApplicationPart(...)) and minimal API endpoints. The expected fixturev1.jsononly contains the controller path/Test— the minimal API path/test/{id}is silently absent. The test passes because it validates against controller output, masking this bug.This test should be restructured to: