Skip to content

Validators for records used with [AsParameters] create unused component schemas #180

@geraldhumphries

Description

@geraldhumphries

When a record is used in an API with [AsParameters] and it has a validator, MicroElements.Swashbuckle.FluentValidation creates unused schemas in the "components" > "schemas" section of the OpenAPI schema for the whole record.

This happens in .NET 10 with v7.0.3 of MicroElements.Swashbuckle.FluentValidation, and v12.0.0 of FluentValidation.

Minimal reproduction: https://github.com/geraldhumphries/fluentvalidation-extraschemas-minimal

Example code:

app.MapGet("/weatherforecast", ([AsParameters] WeatherForecastRequest request) =>
    {
        ...
    })
    .WithName("GetWeatherForecast");

public record WeatherForecastRequest(int Page, int PageSize);

public class WeatherForecastRequestValidator : AbstractValidator<WeatherForecastRequest>
{
    public WeatherForecastRequestValidator()
    {
        RuleFor(x => x.Page).GreaterThan(0);
        RuleFor(x => x.PageSize)
            .GreaterThan(0)
            .LessThanOrEqualTo(500);
    }
};

Resulting schema (the unused schema is named "WeatherForecastRequest"):

Details
{
  "openapi": "3.0.4",
  "info": {
    "title": "OpenAPISchemaRepro",
    "version": "1.0"
  },
  "paths": {
    "/weatherforecast": {
      "get": {
        "tags": [
          "OpenAPISchemaRepro"
        ],
        "operationId": "GetWeatherForecast",
        "parameters": [
          {
            "name": "Page",
            "in": "query",
            "required": true,
            "schema": {
              "minimum": 0,
              "exclusiveMinimum": true,
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "PageSize",
            "in": "query",
            "required": true,
            "schema": {
              "maximum": 500,
              "exclusiveMaximum": false,
              "minimum": 0,
              "exclusiveMinimum": true,
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WeatherForecast"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "WeatherForecast": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "temperatureC": {
            "type": "integer",
            "format": "int32"
          },
          "summary": {
            "type": "string",
            "nullable": true
          },
          "temperatureF": {
            "type": "integer",
            "format": "int32",
            "readOnly": true
          }
        },
        "additionalProperties": false
      },
      "WeatherForecastRequest": {
        "type": "object",
        "properties": {
          "page": {
            "minimum": 0,
            "exclusiveMinimum": true,
            "type": "integer",
            "format": "int32"
          },
          "pageSize": {
            "maximum": 500,
            "minimum": 0,
            "exclusiveMinimum": true,
            "type": "integer",
            "format": "int32"
          }
        },
        "additionalProperties": false
      }
    }
  },
  "tags": [
    {
      "name": "OpenAPISchemaRepro"
    }
  ]
}

The impact of this is that generated clients will get extra unused models, which clutters the API and can be confusing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions