Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JsonTypeInfoExtensions.GetSchemaReferenceId throws ArgumentOutOfRangeException for nested generic types #59092

Open
1 task done
kl1mm opened this issue Nov 21, 2024 · 2 comments
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-openapi help wanted Up for grabs. We would accept a PR to help resolve this issue
Milestone

Comments

@kl1mm
Copy link

kl1mm commented Nov 21, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Hey together,
i have a constellation similar to the following:

...
app.Services.AddOpenApi();
...
app.MapOpenApi();

app.MapGet("/foo", () => new Foo<int>.Bar<string>.Baz { One = 1, Two = "hello" });

public static class Foo<T>
{
    public static class Bar<TT>
    {
        public class Baz
        {
            public required T One { get; set; }
            public required TT Two { get; set; }
        }
    }
}

When I try to access the generated openapi.json in the browser via http://<server>:<port>/openapi/v1.json I get an ArgumentOutOfRangeException.

The problem seems to be located in this part:

// The typesystem says Baz is a generic type but the name property does not have any ` characters 
// because it is nested and the declaring type is the generic one.

if (type.IsGenericType)
{
    var genericTypeName = type.Name[..type.Name.LastIndexOf('`')];
    var genericArguments = type.GetGenericArguments();
    var argumentNames = string.Join("And", genericArguments.Select(arg => arg.GetSchemaReferenceId(options)));
    return $"{genericTypeName}Of{argumentNames}";
}

Expected Behavior

Do not throw an exception and return either Baz or even better FooOfInt.BarOfString.Baz

Steps To Reproduce

No response

Exceptions (if any)

System.ArgumentOutOfRangeException    at System.ArgumentOutOfRangeException.ThrowNegative[T](T value, String paramName)
   at System.ArgumentOutOfRangeException.ThrowIfNegative[T](T value, String paramName)
   at System.String.ThrowSubstringArgumentOutOfRange(Int32 startIndex, Int32 length)
   at System.String.Substring(Int32 startIndex, Int32 length)
   at Microsoft.AspNetCore.OpenApi.JsonTypeInfoExtensions.GetSchemaReferenceId(Type type, JsonSerializerOptions options)
   at Microsoft.AspNetCore.OpenApi.JsonTypeInfoExtensions.GetSchemaReferenceId(JsonTypeInfo jsonTypeInfo, Boolean isTopLevel)
   at Microsoft.AspNetCore.OpenApi.OpenApiOptions.CreateDefaultSchemaReferenceId(JsonTypeInfo jsonTypeInfo)
   at Microsoft.AspNetCore.OpenApi.JsonNodeSchemaExtensions.ApplySchemaReferenceId(JsonNode schema, JsonSchemaExporterContext context, Func`2 createSchemaReferenceId)
   at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.\u003C\u003Ec__DisplayClass0_0.\u003C.ctor\u003Eb__2(JsonSchemaExporterContext context, JsonNode schema)
   at System.Text.Json.Schema.JsonSchema.\u003CToJsonNode\u003Eg__CompleteSchema|104_0(JsonNode schema, \u003C\u003Ec__DisplayClass104_0&)
   at System.Text.Json.Schema.JsonSchema.ToJsonNode(JsonSchemaExporterOptions options)
   at System.Text.Json.Schema.JsonSchemaExporter.GetJsonSchemaAsNode(JsonTypeInfo typeInfo, JsonSchemaExporterOptions exporterOptions)
   at System.Text.Json.Schema.JsonSchemaExporter.GetJsonSchemaAsNode(JsonSerializerOptions options, Type type, JsonSchemaExporterOptions exporterOptions)
   at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.CreateSchema(OpenApiSchemaKey key)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.AspNetCore.OpenApi.OpenApiSchemaStore.GetOrAdd(OpenApiSchemaKey key, Func`2 valueFactory)
   at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.GetOrCreateSchemaAsync(Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription parameterDescription, Boolean captureSchemaByRef, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetResponseAsync(ApiDescription apiDescription, Int32 statusCode, ApiResponseType apiResponseType, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetResponsesAsync(ApiDescription description, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationAsync(ApiDescription description, HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
   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.\u003C\u003Ec__DisplayClass0_0.\u003C\u003CMapOpenApi\u003Eb__0\u003Ed.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Http.Generated.\u003CGeneratedRouteBuilderExtensions_g\u003EF56B68D2B55B5B7B373BA2E4796D897848BC0F04A969B1AF6260183E8B9E0BAF2__GeneratedRouteBuilderExtensionsCore.\u003C\u003Ec__DisplayClass2_0.\u003C\u003CMapGet0\u003Eg__RequestHandler|5\u003Ed.MoveNext()
--- End of stack trace from previous location ---
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.\u003CInvoke\u003Eg__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task)

.NET Version

9.0.100

Anything else?

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Nov 21, 2024
@martincostello martincostello added feature-openapi area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc and removed area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates labels Nov 21, 2024
@captainsafia
Copy link
Member

@kl1mm Thanks for filing this issue!

Would you be interested in submitting a PR to expand our test suite with this example and make a fix to the default implementation for CreateSchemaReferenceId?

@captainsafia captainsafia added the help wanted Up for grabs. We would accept a PR to help resolve this issue label Nov 25, 2024
@captainsafia captainsafia added this to the Backlog milestone Nov 25, 2024
Copy link
Contributor

Looks like this issue has been identified as a candidate for community contribution. If you're considering sending a PR for this issue, look for the Summary Comment link in the issue description. That comment has been left by an engineer on our team to help you get started with handling this issue. You can learn more about our Help Wanted process here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-openapi help wanted Up for grabs. We would accept a PR to help resolve this issue
Projects
None yet
Development

No branches or pull requests

3 participants