Skip to content

Custom CreateSchemaReferenceId not always used in OpenAPI schema $ref #58341

@davidkarlsson

Description

@davidkarlsson

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

If you set a custom OpenApiOptions.CreateSchemaReferenceId delegate it won't use the custom id from it when setting the $ref for a property that has the same type as its declaring type. The reason for this seems to be this code in OpenApiSchemaService where it uses the default JsonTypeInfo.GetSchemaReferenceId extension method instead of the one from settings:

if (jsonPropertyInfo.PropertyType == jsonPropertyInfo.DeclaringType)
{
    return new JsonObject { [OpenApiSchemaKeywords.RefKeyword] = context.TypeInfo.GetSchemaReferenceId() };
}

Expected Behavior

I expected the custom CreateSchemaReferenceId to be used even for nested properties of the same type as its parent which doesn't seem to be the case as far as I can tell.

Steps To Reproduce

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi(options =>
{
    options.CreateSchemaReferenceId = _ => "Overridden";
});

builder.Services.AddControllersWithViews();

var app = builder.Build();

app.UseRouting();

app.UseAuthorization();

app.MapOpenApi();

app.MapControllers();

app.Run();

[ApiController]
[Route("Api/[controller]")]
public class TestController : ControllerBase
{
    [HttpGet("")]
    [ProducesResponseType<Parent>(StatusCodes.Status200OK)]
    public Ok<Parent> Index()
    {
        return TypedResults.Ok(new Parent { RequiredChild = new() });
    }
}

public class Parent
{
    public Child? NullableChild { get; init; }
    public required Child RequiredChild { get; init; }

    public Parent? NestedParent { get; set; }
}

public class Child;

/openapi/v1.json:

{
  "openapi": "3.0.1",
  "info": {
    "title": "OpenApiTest | v1",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://localhost:5001"
    }
  ],
  "paths": {
    "/Api/Test": {
      "get": {
        "tags": [
          "Test"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/Overridden"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Overridden"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/Overridden"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Overridden": {
        "required": [
          "requiredChild"
        ],
        "type": "object",
        "properties": {
          "nullableChild": {
            "$ref": "#/components/schemas/Overridden2"
          },
          "requiredChild": {
            "$ref": "#/components/schemas/Overridden3"
          },
          "nestedParent": {
            "$ref": "#/components/schemas/Parent"
          }
        }
      },
      "Overridden2": {
        "type": "object",
        "nullable": true
      },
      "Overridden3": {
        "type": "object"
      }
    }
  },
  "tags": [
    {
      "name": "Test"
    }
  ]
}

Here I expected the nestedParent $ref to be #/components/schemas/Overridden and not #/components/schemas/Parent since that's what it will reference if you don't override OpenApiOptions.CreateSchemaReferenceId.

Exceptions (if any)

No response

.NET Version

9.0.100-rc.1.24452.12

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-mvcIncludes: MVC, Actions and Controllers, Localization, CORS, most templatesfeature-openapi

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions