dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.55k stars 10.05k forks source link

OpenAPI document generation does not generate correct refs for self-referencing collection properties #58006

Closed null-d3v closed 1 month ago

null-d3v commented 2 months ago

Is there an existing issue for this?

Describe the bug

Microsoft.AspNetCore.OpenApi will generate an invalid JSON schema when a parameter has a self-reference collection property. This does not seem to occur if the self reference is not a collection.

Using the following as a parameter:

public class Criteria
{
    public IEnumerable<Criteria> SubCriteria { get; set; } = [ ];
}

[FromBody] Criteria criteria

Will result in the JSON schema:

  "components": {
    "schemas": {
      "Criteria": {
        "type": "object",
        "properties": {
          "subCriteria": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/#"
            }
          }
        }
      }
    }
  },

Which has an invalid reference:

Resolver error at components.schemas.Criteria.properties.subCriteria.items.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/ does not exist in document

This occurs with both minimal APIs and controllers.

Expected Behavior

I would expect the subCriteria items reference to correctly point at #/components/schemas/Criteria.

Steps To Reproduce

Here is a complete self-contained Program.cs which will generate the invalid OpenAPI document:

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi().CacheOutput();

app.MapPost("/item/search", ([FromBody] Criteria criteria) =>
{
    var items = Enumerable
        .Range(1, 5)
        .Select(index =>
            new Item
            {
                Name = index.ToString(),
            })
        .ToArray();
    return items;
})
.WithName("SearchItems")
.WithOpenApi();

app.Run();

public class Item
{
    public string Name { get; set; } = default!;
}

public class Criteria
{
    public IEnumerable<Criteria> SubCriteria { get; set; } = [ ];
}

And the OpenAPI document generated at /openapi/v1.json:

{
  "openapi": "3.0.1",
  "info": {
    "title": "Sample | v1",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "http://localhost:5129"
    }
  ],
  "paths": {
    "/item/search": {
      "post": {
        "tags": [
          "Sample"
        ],
        "operationId": "SearchItems",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Criteria"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Criteria": {
        "type": "object",
        "properties": {
          "subCriteria": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/#"
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Sample"
    }
  ]
}

Exceptions (if any)

No response

.NET Version

9.0.100-rc.1.24452.12

Anything else?

No response

captainsafia commented 2 months ago

@null-d3v Thanks for reporting this issue!

I'm hoping to drop a fix for this in the upcoming .NET 9 GA release. Once the change is merged, you can try out the fix in one of our nightly releases of the package. If you're interested in learning how to do that, let me know and I can share instructions.

null-d3v commented 2 months ago

@captainsafia Thanks for the prompt reply!! I haven't had to pull from the nightly feed yet, but I've worked out the setup. Very enthused about this in .NET 9! We have a lot of document generation with NSwag that we're anticipating to change.

captainsafia commented 1 month ago

@null-d3v The fix for this has landed in nightly package version 9.0.0-rtm.24476.2.

You'll need to use the following PackageReference:

<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0-rtm.24476.2" />

And make sure that you have a reference to the nightly dotnet9 feed in your nuget.config file:

<add key="dotnet9" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json" />

Can you verify the fix on your end?

null-d3v commented 1 month ago

Sorry for the late reply, this is definitely fixed in 9.0.0-rtm.24476.2