Skip to content

OpenApiGenerator throws ArgumentException if duplicate response model present for a status code #41622

@martincostello

Description

@martincostello

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

If two responses are registered for the same HTTP response code on a Minimal API endpoint using WithOpenApi(), an ArgumentException is thrown, causing an HTTP 500 error, when the endpoint is invoked.

While this is a developer error (registering something twice), the error message could be improved to make it more intuitive to root-cause the problem to fix it.

[2022-05-11 08:52:52Z] fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
      An unhandled exception has occurred while executing the request.
System.ArgumentException: An item with the same key has already been added. Key: 200
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at Microsoft.AspNetCore.OpenApi.OpenApiGenerator.GetOpenApiResponses(MethodInfo method, EndpointMetadataCollection metadata)
   at Microsoft.AspNetCore.OpenApi.OpenApiGenerator.GetOperation(String httpMethod, MethodInfo methodInfo, EndpointMetadataCollection metadata, RoutePattern pattern)
   at Microsoft.AspNetCore.OpenApi.OpenApiGenerator.GetOpenApiOperation(MethodInfo methodInfo, EndpointMetadataCollection metadata, RoutePattern pattern)
   at Microsoft.AspNetCore.OpenApi.OpenApiRouteHandlerBuilderExtensions.GetOperationForEndpoint(RouteEndpointBuilder routeEndpointBuilder)
   at Microsoft.AspNetCore.OpenApi.OpenApiRouteHandlerBuilderExtensions.<>c__DisplayClass1_0.<WithOpenApi>b__0(EndpointBuilder endpointBuilder)
   at Microsoft.AspNetCore.Routing.DefaultEndpointConventionBuilder.Build()
   at System.Linq.Enumerable.SelectListIterator`2.ToArray()
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.ToArray()
   at Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.Initialize()
   at Microsoft.AspNetCore.Routing.DataSourceDependentCache`1.Initialize()
   at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
   at Microsoft.AspNetCore.Routing.Matching.DataSourceDependentMatcher..ctor(EndpointDataSource dataSource, Lifetime lifetime, Func`1 matcherBuilderFactory)
   at Microsoft.AspNetCore.Routing.Matching.DfaMatcherFactory.CreateMatcher(EndpointDataSource dataSource)
   at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.InitializeCoreAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.<Invoke>g__AwaitMatcher|8_0(EndpointRoutingMiddleware middleware, HttpContext httpContext, Task`1 matcherTask)
   at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)
   at MartinCostello.Api.Middleware.CustomHttpHeadersMiddleware.Invoke(HttpContext context) in C:\Coding\martincostello\api\src\API\Middleware\CustomHttpHeadersMiddleware.cs:line 111
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

This appears to come from one of these two locations:

eligibileAnnotations.Add(statusCode, (discoveredTypeAnnotation, discoveredContentTypeAnnotation));

eligibileAnnotations.Add(statusCode, (discoveredTypeAnnotation, discoveredContentTypeAnnotation));

Expected Behavior

A more descriptive exception message is thrown to guide the developer as to what the duplicate registration is (e.g. including the endpoint, type and/or content-type).

Steps To Reproduce

Register an HTTP endpoint with Minimal APIs and use both Produces<T>() and WithOpenApi().

For example:

app.MapGet("/foo", () => TypedResults.Ok(new MyModel()))
   .Produces<MyModel>()
   .WithOpenApi();

Exceptions (if any)

System.ArgumentException: An item with the same key has already been added. Key: 200
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at Microsoft.AspNetCore.OpenApi.OpenApiGenerator.GetOpenApiResponses(MethodInfo method, EndpointMetadataCollection metadata)
   at Microsoft.AspNetCore.OpenApi.OpenApiGenerator.GetOperation(String httpMethod, MethodInfo methodInfo, EndpointMetadataCollection metadata, RoutePattern pattern)
   at Microsoft.AspNetCore.OpenApi.OpenApiGenerator.GetOpenApiOperation(MethodInfo methodInfo, EndpointMetadataCollection metadata, RoutePattern pattern)
   at Microsoft.AspNetCore.OpenApi.OpenApiRouteHandlerBuilderExtensions.GetOperationForEndpoint(RouteEndpointBuilder routeEndpointBuilder)
   at Microsoft.AspNetCore.OpenApi.OpenApiRouteHandlerBuilderExtensions.<>c__DisplayClass1_0.<WithOpenApi>b__0(EndpointBuilder endpointBuilder)
   at Microsoft.AspNetCore.Routing.DefaultEndpointConventionBuilder.Build()
   at System.Linq.Enumerable.SelectListIterator`2.ToArray()
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.ToArray()
   at Microsoft.AspNetCore.Routing.CompositeEndpointDataSource.Initialize()
   at Microsoft.AspNetCore.Routing.DataSourceDependentCache`1.Initialize()
   at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
   at Microsoft.AspNetCore.Routing.Matching.DataSourceDependentMatcher..ctor(EndpointDataSource dataSource, Lifetime lifetime, Func`1 matcherBuilderFactory)
   at Microsoft.AspNetCore.Routing.Matching.DfaMatcherFactory.CreateMatcher(EndpointDataSource dataSource)
   at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.InitializeCoreAsync()

.NET Version

7.0.100-preview.4.22252.9

Anything else?

No response

Metadata

Metadata

Assignees

Labels

bugThis issue describes a behavior which is not expected - a bug.feature-openapiold-area-web-frameworks-do-not-use*DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions