Is there an existing issue for this?
Describe the bug
If Microsoft.AspNetCore.OpenApi is used to document an endpoint that does not have an explicit HTTP method (e.g. with [HttpGet]) then rendering the OpenAPI document fails with an HTTP 500 due to the following exception:
[2025-03-13 13:15:45Z] fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Unsupported HTTP method:
at ApiDescriptionExtensions.GetOperationType(ApiDescription apiDescription)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationsAsync(IGrouping`2 descriptions, HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiPathsAsync(HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiDocumentAsync(IServiceProvider scopedServiceProvider, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Builder.OpenApiEndpointRouteBuilderExtensions.<>c__DisplayClass0_0.<<MapOpenApi>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Http.Generated.<GeneratedRouteBuilderExtensions_g>F56B68D2B55B5B7B373BA2E4796D897848BC0F04A969B1AF6260183E8B9E0BAF2__GeneratedRouteBuilderExtensionsCore.<>c__DisplayClass2_0.<<MapGet0>g__RequestHandler|5>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
The exception is coming from here:
|
public static OperationType GetOperationType(this ApiDescription apiDescription) => |
|
apiDescription.HttpMethod?.ToUpperInvariant() switch |
|
{ |
|
"GET" => OperationType.Get, |
|
"POST" => OperationType.Post, |
|
"PUT" => OperationType.Put, |
|
"DELETE" => OperationType.Delete, |
|
"PATCH" => OperationType.Patch, |
|
"HEAD" => OperationType.Head, |
|
"OPTIONS" => OperationType.Options, |
|
"TRACE" => OperationType.Trace, |
|
_ => throw new InvalidOperationException($"Unsupported HTTP method: {apiDescription.HttpMethod}"), |
|
}; |
My hunch is that the operation isn't set by MVC in this case, with the rest of the framework treating it as a GET, but that isn't catered for by the switch statement, which then throws an exception.
The exception is easily avoided by adding the relevant attribute, such as:
using WebApi.Models;
using Microsoft.AspNetCore.Mvc;
namespace WebApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class TimeController(TimeProvider timeProvider) : ControllerBase
{
+ [HttpGet]
public ActionResult<TimeModel> Now()
=> Ok(new TimeModel { UtcNow = timeProvider.GetUtcNow() });
}
Expected Behavior
The endpoint is rendered in the OpenAPI document as an HTTP GET to match MVC's behaviour when the endpoint is invoked.
Steps To Reproduce
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<RootNamespace>WebApi</RootNamespace>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
</ItemGroup>
</Project>
using System.Text.Json;
using WebApi;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(TimeProvider.System);
builder.Services.AddControllers().AddJsonOptions((p) => ConfigureJsonSerialization(p.JsonSerializerOptions));
builder.Services.ConfigureHttpJsonOptions((p) => ConfigureJsonSerialization(p.SerializerOptions));
builder.Services.AddOpenApi();
var app = builder.Build();
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Time}/{action=Now}/{id?}");
app.MapOpenApi();
app.Run();
static void ConfigureJsonSerialization(JsonSerializerOptions options)
=> options.TypeInfoResolverChain.Add(MvcSerializerContext.Default);
using WebApi.Models;
using Microsoft.AspNetCore.Mvc;
namespace WebApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class TimeController(TimeProvider timeProvider) : ControllerBase
{
public ActionResult<TimeModel> Now()
=> Ok(new TimeModel { UtcNow = timeProvider.GetUtcNow() });
}
namespace WebApi.Models;
public class TimeModel
{
public DateTimeOffset UtcNow { get; set; }
}
using System.Text.Json.Serialization;
using WebApi.Models;
namespace WebApi;
[JsonSerializable(typeof(DateTimeOffset))]
[JsonSerializable(typeof(TimeModel))]
[JsonSourceGenerationOptions(
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
WriteIndented = true)]
public sealed partial class MvcSerializerContext : JsonSerializerContext;
Exceptions (if any)
[2025-03-13 13:15:45Z] fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Unsupported HTTP method:
at ApiDescriptionExtensions.GetOperationType(ApiDescription apiDescription)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationsAsync(IGrouping`2 descriptions, HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiPathsAsync(HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiDocumentAsync(IServiceProvider scopedServiceProvider, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Builder.OpenApiEndpointRouteBuilderExtensions.<>c__DisplayClass0_0.<<MapOpenApi>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Http.Generated.<GeneratedRouteBuilderExtensions_g>F56B68D2B55B5B7B373BA2E4796D897848BC0F04A969B1AF6260183E8B9E0BAF2__GeneratedRouteBuilderExtensionsCore.<>c__DisplayClass2_0.<<MapGet0>g__RequestHandler|5>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
.NET Version
9.0.201
Anything else?
No response
Is there an existing issue for this?
Describe the bug
If Microsoft.AspNetCore.OpenApi is used to document an endpoint that does not have an explicit HTTP method (e.g. with
[HttpGet]) then rendering the OpenAPI document fails with an HTTP 500 due to the following exception:The exception is coming from here:
aspnetcore/src/OpenApi/src/Extensions/ApiDescriptionExtensions.cs
Lines 20 to 32 in f470910
My hunch is that the operation isn't set by MVC in this case, with the rest of the framework treating it as a GET, but that isn't catered for by the switch statement, which then throws an exception.
The exception is easily avoided by adding the relevant attribute, such as:
using WebApi.Models; using Microsoft.AspNetCore.Mvc; namespace WebApi.Controllers; [ApiController] [Route("api/[controller]")] public class TimeController(TimeProvider timeProvider) : ControllerBase { + [HttpGet] public ActionResult<TimeModel> Now() => Ok(new TimeModel { UtcNow = timeProvider.GetUtcNow() }); }Expected Behavior
The endpoint is rendered in the OpenAPI document as an HTTP GET to match MVC's behaviour when the endpoint is invoked.
Steps To Reproduce
Exceptions (if any)
.NET Version
9.0.201
Anything else?
No response