domaindrivendev / Swashbuckle.AspNetCore

Swagger tools for documenting API's built on ASP.NET Core
MIT License
5.22k stars 1.31k forks source link

[Bug]: /swagger/index.html website is frozen when provided specific argument from route as a SwaggerParameter #3083

Open PFYasu opened 6 days ago

PFYasu commented 6 days ago

Describe the bug

Hello,

Below is my endpoint signature:

        [SwaggerOperation(
            Summary = "Terminate product",
            OperationId = SwaggerOperationIds.Products.Terminate,
            Tags = [SwaggerTags.Products]
        )]
        [SwaggerResponseAccepted]
        [SwaggerResponseBadRequest]
        [SwaggerResponseUnauthorized]
        [SwaggerResponseNotFound]
        [SwaggerResponseGone]
        [AuditLog]
        [Route("/api/v1/products/terminate/{id}")]
        [HttpPost]
        public async Task<IActionResult> Terminate(
            [SwaggerParameter("Product ID", Required = true)][FromRoute] MyType<Product> id,
            [SwaggerParameter("Terminate product request", Required = false)][FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] TerminateProductRequest request)
        {
           ...

And this is the MyType class:

public class MyType<TType>
{
    public Type Value => typeof(TType);
}

For this configuration I've got an infinity loop in SwaggerGenerator class.

private (IEnumerable<ApiDescription>, OpenApiDocument, SchemaRepository) GetSwaggerDocumentWithoutFilters(string documentName, string host = null, string basePath = null)
        {
            if (!_options.SwaggerDocs.TryGetValue(documentName, out OpenApiInfo info))
                throw new UnknownSwaggerDocument(documentName, _options.SwaggerDocs.Select(d => d.Key));

            // THE LOOP IS IN THE METHOD BELOW AND IT CONSUMES ALL OF MY RAM (after 1 minute of request processing, it ate ~ 20 GB of RAM)

            var applicableApiDescriptions = _apiDescriptionsProvider.ApiDescriptionGroups.Items  
                .SelectMany(group => group.Items)
                .Where(apiDesc =>
                {
                    var attributes = apiDesc.CustomAttributes().ToList();
                    return !(_options.IgnoreObsoleteActions && attributes.OfType<ObsoleteAttribute>().Any()) &&
                           !attributes.OfType<SwaggerIgnoreAttribute>().Any() &&
                           _options.DocInclusionPredicate(documentName, apiDesc);
                });

            var schemaRepository = new SchemaRepository(documentName);

            var swaggerDoc = new OpenApiDocument
            {
                Info = info,
                Servers = GenerateServers(host, basePath),
                Paths = GeneratePaths(applicableApiDescriptions, schemaRepository),
                Components = new OpenApiComponents
                {
                    Schemas = schemaRepository.Schemas,
                },
                SecurityRequirements = new List<OpenApiSecurityRequirement>(_options.SecurityRequirements)
            };

            return (applicableApiDescriptions, swaggerDoc, schemaRepository);
        }

Note in the class ctor I cannot read ApiDescriptionGroups from ApiDescriptionProvider.

image

If I change my affected class a bit as shown below:

public class MyType<TType>
{
    public Type Value = typeof(TType);  // CHANGED `=>` to `=`
}

everything works fine

image

Expected behavior

Swagger should load the page with endpoint descriptions.

Actual behavior

It is spinning and consuming RAM image

Steps to reproduce

No response

Exception(s) (if any)

No response

Swashbuckle.AspNetCore version

6.6.2

.NET Version

.NET 8.0

Anything else?

No response

martincostello commented 6 days ago

Annotate the property (as I assume it's for use server-side, not for clients in your contract) with the [JsonIgnore] attribute so that you aren't trying to represent a System.Type in your schema (because that's never going to work).

I'm reasonably confident this is an issue with your code (trying to put things that shouldn't be in the OpenAPI document into the OpenAPI document) and not a bug in either Swashbuckle nor swagger-ui.