NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
NuGet · #1107 most downloaded on NuGet
Swagger ISchemaFilter that uses FluentValidation validators instead System.ComponentModel based attributes.
Last release 1 months ago
21 Aug 2026
Release timing varies
gaps range from 8 days to 2 months
Nearly every release is documented
notes for 56 of 60 stable releases
Nothing withdrawn
no release was ever pulled
9 years old
62 releases · first in 2018
…minimum would force the Microsoft.OpenApi 2.x breaking change on consumers. The throwaway recovery from #223 remains as a safety net on all targets
$# Changes in 7.2.2
MicroElements.AspNetCore.OpenApi.FluentValidation did not apply FluentValidation rules to a [FromForm] DTO bound by an MVC controller action (Issue #232, follow-up to the Swashbuckle-only #170 fix)
ApiExplorer flattens a controller's [FromForm] complex parameter into one form field per property, so Microsoft.AspNetCore.OpenApi builds the request body as an inline schema from those descriptions and never asks for the DTO's JsonTypeInfo — FluentValidationSchemaTransformer therefore never saw the type. Minimal API form bodies are not flattened and were never affectedFluentValidationOperationTransformer now applies the DTO's rules to the inline form schema for both multipart/form-data and application/x-www-form-urlencoded. The flattening is detected via ModelMetadata.ContainerType on the form-bound parameter descriptions, so a minimal API body — which the schema transformer already owns — is never touched twiceName, not name); the validator-side match stays name-insensitiveallOf of one property bag per parameter; every bag is now constrained, and each bag is paired with the parameter that owns its fields — including the parameters the action binds directly (a loose scalar, array or IFormFile), which get a bag of their own but no rules. Without that pairing a DTO could be pulled into a foreign bag by a single coinciding field name and lose the constraints on its own bagencoding.contentType (Issue #216) is now also emitted when the file part sits inside such an allOf bagInner.City, ApiExplorer's binding path for a nested complex property) are skipped: the name matcher ignores separators, so such a key would otherwise inherit the rules of an unrelated root property named InnerCity. Nested form fields remain unsupported, matching the Swashbuckle form pathencoding.contentType is unchanged for the shapes it already covered, and now also consults every validator in multi-validator mode (IsOneValidatorForType = false) instead of only the first[FromForm(Name = "...")], or a property renamed by the INameResolver) gets no constraints, and on net10 a form part rendered as a $ref (IFormFile, enums) cannot receive property-level constraintsUseDocumentFilter = true) did not apply FluentValidation constraints to aliased operation parameters such as [FromHeader(Name = "X-Correlation-Id")] (Issue #230). The constraint copy now uses the resolved schema property key (kebab-case/camelCase/PascalCase aliases) and falls back to the INameResolver for renames beyond separators (e.g. [JsonPropertyName]) — full parity with the operation filter[FromHeader(Name = "X.Trace.Id")] — legal in HTTP) was treated as a nested [FromQuery] dot-path (#209/#211) and silently dropped the header's validation rules. Header-bound parameters are now exempt from the dot-path logic in all three parameter pipelines: FluentValidationOperationFilter, FluentValidationDocumentFilter and the ASP.NET Core FluentValidationOperationTransformer. NSwag is unaffected (it has no dot-path parameter logic)ApiExplorer descriptions case-sensitively, so options like DescribeAllParametersInCamelCase made it skip required-marking and constraint copying. The lookup is now case-insensitive, matching the operation filterRegistrationOptions.UseDocumentFilter, default false). The document filter processes the whole document at once and performs the unused-query-schema cleanup once at the end, so per-operation shared-DTO state issues (#223/#226) cannot occur in this pipeline — on all target frameworks, including net8.0/net9.0 where the 7.1.11 healing API is unavailable
encoding.contentType for [FromForm] (#216), multi-validator support, allOf/oneOf/anyOf traversal, $ref preservation for unmodified properties (#198, net10.0), and every operation of a multi-verb path is now processed (previously only the first)ServiceProviderValidatorRegistry like the sibling filters, honors an injected IFluentValidationRuleProvider (new optional constructor parameter, appended last — source-compatible), and had its dead code, logging and nullability issues cleaned up. Behavior note: constructing FluentValidationDocumentFilter with neither validatorRegistry nor serviceProvider now throws ArgumentNullException (matching the sibling filters) instead of silently producing a filter that applies no rules; the DI registration path is unaffectedSchemaRepository.ReplaceSchemaId), so custom document filters running afterwards can regenerate themExperimentalUseDocumentFilter still works as an [Obsolete] alias forwarding to UseDocumentFiltersamples/MinimalApi now runs on the document-filter pipeline; README documents the option and its caveats[FromQuery]/[AsParameters] binding and a request body ([FromBody]/[FromForm]) in the same document could lose its FluentValidation rules, and the emitted document could contain a $ref to a removed component (Issue #226). net10.0 target only
SchemaRepository (component removed, internal reserved-id kept). The 7.1.10 fix (#223) recovered the schema for reading constraint values, but a later [FromBody]/[FromForm] operation binding the same type made Swashbuckle emit a $ref to a component that no longer exists (confirmed empirically), and rules applied on the recovered throwaway instance never reached the documentSchemaRepository.ReplaceSchemaId (public API since Swashbuckle 10.1.0) before removing a side-effect schema — the reservation is cleared together with the component, so the next operation binding the same type regenerates a full component and the rules reach the real document object. SwashbuckleSchemaProvider tracks Type → schemaId for every GetSchemaForType call (including the Issue #209 ancestor walk) to make the healing possibleReplaceSchemaId does not exist, and bumping the minimum would force the Microsoft.OpenApi 2.x breaking change on consumers. The throwaway recovery from #223 remains as a safety net on all targetsReplaceSchemaId approach[FromQuery]/[AsParameters] DTO shared by more than one endpoint lost its FluentValidation rules on every endpoint after the first (Issue #223)
FluentValidationOperationFilter runs once per operation. The Issue #180 cleanup removes the temporary container schema from SchemaRepository.Schemas, but Swashbuckle keeps the type in its internal reserved-ids map, which the cleanup does not touch. For the 2nd+ endpoint, GetSchemaForType → GenerateSchema then returns a bare $ref (no Properties) and the schema is no longer in Schemas, so the "has properties" guard was skipped and no rules were applied. Only reproduces with the default RemoveUnusedQuerySchemas = trueSwashbuckleSchemaProvider.GetSchemaForType, when the returned schema has no properties and the id is absent from the shared repository (the reserved-but-removed state), the concrete schema is recovered by generating it into a throwaway SchemaRepository. Fully isolated — it never mutates the shared repository or its reserved-id state — so the Issue #180 cleanup and all other OperationFilter behavior (required marking #209, nested [FromQuery] #211/#213, request bodies, #216) are preservedFull release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
One column per quarter.
[FromForm] rules were ignored for MVC controller actionsReported in #232 by @bux, as a follow-up to the Swashbuckle-only fix in #170.
Affects MicroElements.AspNetCore.OpenApi.FluentValidation — the integration with the native Microsoft.AspNetCore.OpenApi generator. Minimal APIs were never affected.
[ApiController]
[Route("api/customers")]
public class CustomersController : ControllerBase
{
[HttpPost]
public IActionResult Post([FromForm] CustomerDto dto) => Ok(); // <- rules were silently dropped
}Root cause: MVC ApiExplorer flattens a controller's [FromForm] complex parameter into one ApiParameterDescription per form field. The generator then builds the request body as an inline schema from those descriptions and never asks for the DTO's JsonTypeInfo — so FluentValidationSchemaTransformer, which hooks into schema generation by type, never saw the DTO at all. A minimal-API form body is not flattened: it gets a $ref to a component schema, which is why that path worked.
FluentValidationOperationTransformer now applies the DTO's rules to the inline form schema, for both multipart/form-data and application/x-www-form-urlencoded. The flattening is detected via ModelMetadata.ContainerType, so a minimal-API body — already owned by the schema transformer — is never processed twice.
Before / after, for Name: NotEmpty().MaximumLength(42) and Age: GreaterThanOrEqualTo(7).LessThanOrEqualTo(99):
"application/x-www-form-urlencoded": {
"schema": {
+ "required": ["Name"],
"type": "object",
"properties": {
- "Name": { "type": "string" },
- "Age": { "type": ["integer","string"] }
+ "Name": { "minLength": 1, "maxLength": 42, "type": "string" },
+ "Age": { "minimum": 7, "maximum": 99, "type": ["integer","string"] }
}
}
}An action binding more than one form parameter composes its request body as an allOf of one property bag per parameter. Every bag is now constrained, and each is paired with the parameter that owns its fields — so two DTOs that happen to declare a property with the same name cannot exchange constraints, and a parameter the action binds directly (a loose scalar, array or IFormFile) keeps its own bag free of any DTO's rules.
ApiExplorer names a flattened nested property with its binding path (Inner.City). Those keys are deliberately skipped: the rule matcher ignores separators, so such a key would otherwise inherit the rules of an unrelated root property named InnerCity. Nested form fields remain unsupported, matching the Swashbuckle form path.
encoding.contentType (#216) is no longer dropped when an action binds more than one form parameter — the file-part lookup now walks the allOf bags.GetValidators) rather than only the first, so multi-validator mode (IsOneValidatorForType = false) behaves like the schema path.[FromForm(Name = "...")], or a property renamed by the INameResolver — gets no constraints. Same class as #230 was for parameters.$ref (IFormFile, enums) cannot receive property-level constraints; required still lands correctly.IFormFile erases the #216 file-part notes document-wide (#234, with a full analysis of the mechanism).Patch release — no public API changes. Swashbuckle and NSwag are unaffected.
Full changelog: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
…minimum would force the Microsoft.OpenApi 2.x breaking change on consumers. The throwaway recovery from #223 remains as a safety net on all targets
$# Changes in 7.2.1
UseDocumentFilter = true) did not apply FluentValidation constraints to aliased operation parameters such as [FromHeader(Name = "X-Correlation-Id")] (Issue #230). The constraint copy now uses the resolved schema property key (kebab-case/camelCase/PascalCase aliases) and falls back to the INameResolver for renames beyond separators (e.g. [JsonPropertyName]) — full parity with the operation filter[FromHeader(Name = "X.Trace.Id")] — legal in HTTP) was treated as a nested [FromQuery] dot-path (#209/#211) and silently dropped the header's validation rules. Header-bound parameters are now exempt from the dot-path logic in all three parameter pipelines: FluentValidationOperationFilter, FluentValidationDocumentFilter and the ASP.NET Core FluentValidationOperationTransformer. NSwag is unaffected (it has no dot-path parameter logic)ApiExplorer descriptions case-sensitively, so options like DescribeAllParametersInCamelCase made it skip required-marking and constraint copying. The lookup is now case-insensitive, matching the operation filterRegistrationOptions.UseDocumentFilter, default false). The document filter processes the whole document at once and performs the unused-query-schema cleanup once at the end, so per-operation shared-DTO state issues (#223/#226) cannot occur in this pipeline — on all target frameworks, including net8.0/net9.0 where the 7.1.11 healing API is unavailable
encoding.contentType for [FromForm] (#216), multi-validator support, allOf/oneOf/anyOf traversal, $ref preservation for unmodified properties (#198, net10.0), and every operation of a multi-verb path is now processed (previously only the first)ServiceProviderValidatorRegistry like the sibling filters, honors an injected IFluentValidationRuleProvider (new optional constructor parameter, appended last — source-compatible), and had its dead code, logging and nullability issues cleaned up. Behavior note: constructing FluentValidationDocumentFilter with neither validatorRegistry nor serviceProvider now throws ArgumentNullException (matching the sibling filters) instead of silently producing a filter that applies no rules; the DI registration path is unaffectedSchemaRepository.ReplaceSchemaId), so custom document filters running afterwards can regenerate themExperimentalUseDocumentFilter still works as an [Obsolete] alias forwarding to UseDocumentFiltersamples/MinimalApi now runs on the document-filter pipeline; README documents the option and its caveats[FromQuery]/[AsParameters] binding and a request body ([FromBody]/[FromForm]) in the same document could lose its FluentValidation rules, and the emitted document could contain a $ref to a removed component (Issue #226). net10.0 target only
SchemaRepository (component removed, internal reserved-id kept). The 7.1.10 fix (#223) recovered the schema for reading constraint values, but a later [FromBody]/[FromForm] operation binding the same type made Swashbuckle emit a $ref to a component that no longer exists (confirmed empirically), and rules applied on the recovered throwaway instance never reached the documentSchemaRepository.ReplaceSchemaId (public API since Swashbuckle 10.1.0) before removing a side-effect schema — the reservation is cleared together with the component, so the next operation binding the same type regenerates a full component and the rules reach the real document object. SwashbuckleSchemaProvider tracks Type → schemaId for every GetSchemaForType call (including the Issue #209 ancestor walk) to make the healing possibleReplaceSchemaId does not exist, and bumping the minimum would force the Microsoft.OpenApi 2.x breaking change on consumers. The throwaway recovery from #223 remains as a safety net on all targetsReplaceSchemaId approach[FromQuery]/[AsParameters] DTO shared by more than one endpoint lost its FluentValidation rules on every endpoint after the first (Issue #223)
FluentValidationOperationFilter runs once per operation. The Issue #180 cleanup removes the temporary container schema from SchemaRepository.Schemas, but Swashbuckle keeps the type in its internal reserved-ids map, which the cleanup does not touch. For the 2nd+ endpoint, GetSchemaForType → GenerateSchema then returns a bare $ref (no Properties) and the schema is no longer in Schemas, so the "has properties" guard was skipped and no rules were applied. Only reproduces with the default RemoveUnusedQuerySchemas = trueSwashbuckleSchemaProvider.GetSchemaForType, when the returned schema has no properties and the id is absent from the shared repository (the reserved-but-removed state), the concrete schema is recovered by generating it into a throwaway SchemaRepository. Fully isolated — it never mutates the shared repository or its reserved-id state — so the Issue #180 cleanup and all other OperationFilter behavior (required marking #209, nested [FromQuery] #211/#213, request bodies, #216) are preservedshort/byte/ushort/uint/ulong/sbyte-typed rules were silently dropped (Issue #222)
IsNumeric in the shared core (MicroElements.OpenApi.FluentValidation) only recognized int/long/float/double/decimal/BigInteger, so a Between/Comparison rule whose bound was a small integer type (e.g. InclusiveBetween((short)1, (short)99)) matched but produced no minimum/maximum — even though the generator emits "type": "integer" for the property. This could not be worked around at the validator definition site because those rule overloads only accept bounds of the property's own typeIsNumeric now recognizes all integer primitives (sbyte/byte/short/ushort/int/uint/long/ulong) in addition to the floating/decimal/BigInteger types; NumericToDecimal already converted them all. One change in the shared core fixes all three providers (Swashbuckle, NSwag, and the native Microsoft.AspNetCore.OpenApi transformer)Full release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
Reported in #230 by @jgarciadelanoceda.
With the document-filter pipeline enabled (UseDocumentFilter = true), constraints were not applied to aliased operation parameters:
public sealed class HelloRequest
{
[FromQuery]
public string? Name { get; set; }
[FromHeader(Name = "X-Correlation-Id")] // <- rules were silently dropped
public string? XCorrelationId { get; set; }
}Root cause: the document filter resolved the schema property key for the alias ("X-Correlation-Id" → XCorrelationId) but used it only for required-marking, while the constraint copy still looked the property up by the raw alias — and never found it. The default operation-filter pipeline handled this correctly, so the two pipelines disagreed. The constraint copy now uses the resolved key and additionally falls back to the INameResolver for renames beyond separators (e.g. [JsonPropertyName]) — full parity with the operation filter.
A dot is legal in an HTTP header name, but [FromHeader(Name = "X.Trace.Id")] was mistaken for a flattened nested [FromQuery] dot-path (#209/#211): the name was truncated to the last segment (Id), matched no property, and the rules disappeared. Header-bound parameters are now exempt from the dot-path logic in all three parameter pipelines — FluentValidationOperationFilter, FluentValidationDocumentFilter and the ASP.NET Core FluentValidationOperationTransformer. This one affected the default pipeline too, not just the document filter. NSwag is unaffected (it has no dot-path parameter logic).
The document filter matched document parameters to their ApiExplorer descriptions case-sensitively, so options such as DescribeAllParametersInCamelCase made it skip both required-marking and the constraint copy. The lookup is now case-insensitive, matching the operation filter.
Patch release — no public API changes. The constraint-copy fallback in the ASP.NET Core transformer was also aligned with the other two pipelines, so all three now resolve property names identically.
Full changelog: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). Th…
$# Changes in 7.2.0
RegistrationOptions.UseDocumentFilter, default false). The document filter processes the whole document at once and performs the unused-query-schema cleanup once at the end, so per-operation shared-DTO state issues (#223/#226) cannot occur in this pipeline — on all target frameworks, including net8.0/net9.0 where the 7.1.11 healing API is unavailable
encoding.contentType for [FromForm] (#216), multi-validator support, allOf/oneOf/anyOf traversal, $ref preservation for unmodified properties (#198, net10.0), and every operation of a multi-verb path is now processed (previously only the first)ServiceProviderValidatorRegistry like the sibling filters, honors an injected IFluentValidationRuleProvider (new optional constructor parameter, appended last — source-compatible), and had its dead code, logging and nullability issues cleaned up. Behavior note: constructing FluentValidationDocumentFilter with neither validatorRegistry nor serviceProvider now throws ArgumentNullException (matching the sibling filters) instead of silently producing a filter that applies no rules; the DI registration path is unaffectedSchemaRepository.ReplaceSchemaId), so custom document filters running afterwards can regenerate themExperimentalUseDocumentFilter still works as an [Obsolete] alias forwarding to UseDocumentFiltersamples/MinimalApi now runs on the document-filter pipeline; README documents the option and its caveats[FromQuery]/[AsParameters] binding and a request body ([FromBody]/[FromForm]) in the same document could lose its FluentValidation rules, and the emitted document could contain a $ref to a removed component (Issue #226). net10.0 target only
SchemaRepository (component removed, internal reserved-id kept). The 7.1.10 fix (#223) recovered the schema for reading constraint values, but a later [FromBody]/[FromForm] operation binding the same type made Swashbuckle emit a $ref to a component that no longer exists (confirmed empirically), and rules applied on the recovered throwaway instance never reached the documentSchemaRepository.ReplaceSchemaId (public API since Swashbuckle 10.1.0) before removing a side-effect schema — the reservation is cleared together with the component, so the next operation binding the same type regenerates a full component and the rules reach the real document object. SwashbuckleSchemaProvider tracks Type → schemaId for every GetSchemaForType call (including the Issue #209 ancestor walk) to make the healing possibleReplaceSchemaId does not exist, and bumping the minimum would force the Microsoft.OpenApi 2.x breaking change on consumers. The throwaway recovery from #223 remains as a safety net on all targetsReplaceSchemaId approach[FromQuery]/[AsParameters] DTO shared by more than one endpoint lost its FluentValidation rules on every endpoint after the first (Issue #223)
FluentValidationOperationFilter runs once per operation. The Issue #180 cleanup removes the temporary container schema from SchemaRepository.Schemas, but Swashbuckle keeps the type in its internal reserved-ids map, which the cleanup does not touch. For the 2nd+ endpoint, GetSchemaForType → GenerateSchema then returns a bare $ref (no Properties) and the schema is no longer in Schemas, so the "has properties" guard was skipped and no rules were applied. Only reproduces with the default RemoveUnusedQuerySchemas = trueSwashbuckleSchemaProvider.GetSchemaForType, when the returned schema has no properties and the id is absent from the shared repository (the reserved-but-removed state), the concrete schema is recovered by generating it into a throwaway SchemaRepository. Fully isolated — it never mutates the shared repository or its reserved-id state — so the Issue #180 cleanup and all other OperationFilter behavior (required marking #209, nested [FromQuery] #211/#213, request bodies, #216) are preservedshort/byte/ushort/uint/ulong/sbyte-typed rules were silently dropped (Issue #222)
IsNumeric in the shared core (MicroElements.OpenApi.FluentValidation) only recognized int/long/float/double/decimal/BigInteger, so a Between/Comparison rule whose bound was a small integer type (e.g. InclusiveBetween((short)1, (short)99)) matched but produced no minimum/maximum — even though the generator emits "type": "integer" for the property. This could not be worked around at the validator definition site because those rule overloads only accept bounds of the property's own typeIsNumeric now recognizes all integer primitives (sbyte/byte/short/ushort/int/uint/long/ulong) in addition to the floating/decimal/BigInteger types; NumericToDecimal already converted them all. One change in the shared core fixes all three providers (Swashbuckle, NSwag, and the native Microsoft.AspNetCore.OpenApi transformer)Swashbuckle.AspNetCore.SwaggerGen on the net10.0 target was bumped 10.0.0 → 10.2.1, which resolves Microsoft.OpenApi to the patched 2.7.5 (was 2.3.0). This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). The net8.0/net9.0 targets use Swashbuckle 8.1.1 → Microsoft.OpenApi v1 and were never in the advisory rangeIFormFile uploads (Issue #216): stable rollup of everything in 7.1.8-beta.1 and 7.1.8-beta.2 below (new File-level rules .FileContentType(), .MaxFileSize(), .MinFileSize(), .FileSizeBetween(); Swashbuckle / NSwag / Microsoft.AspNetCore.OpenApi emit multipart encoding.contentType and description annotations)Full release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
UseDocumentFilter)services.AddFluentValidationRulesToSwagger(
configureRegistration: options => options.UseDocumentFilter = true);The document filter processes the whole OpenAPI document at once — component schemas, operation parameters and request bodies — and performs the unused-query-schema cleanup once at the end. Because there is no per-operation state, the shared-DTO bug class (#223, #226) cannot occur in this pipeline, on any target framework — including net8.0/net9.0, where the 7.1.11 fix could not fully apply (the ReplaceSchemaId healing API only exists in Swashbuckle 10.1.0+).
Previously the filter was experimental with significant gaps. Now it matches the default schema + operation filter pipeline:
encoding.contentType for [FromForm] uploads (#216)allOf/oneOf/anyOf traversal$ref preservation for properties untouched by rules (#198, net10.0)The #209 and #216 logic is shared between both pipelines via internal components, so they cannot drift apart.
A throwing validator no longer fails the whole document generation; the filter falls back to ServiceProviderValidatorRegistry like the sibling filters and honors an injected IFluentValidationRuleProvider (new optional constructor parameter, appended last — source-compatible). Behavior note: constructing the filter directly with neither validatorRegistry nor serviceProvider now throws ArgumentNullException instead of silently applying no rules; the DI path is unaffected.
UseDocumentFilter defaults to false. A future major version may switch the default.ExperimentalUseDocumentFilter still works as an [Obsolete] alias forwarding to UseDocumentFilter.samples/MinimalApi now runs on the document-filter pipeline; the README documents the option and its caveats.Full changelog: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). Th…
$# Changes in 7.1.11
[FromQuery]/[AsParameters] binding and a request body ([FromBody]/[FromForm]) in the same document could lose its FluentValidation rules, and the emitted document could contain a $ref to a removed component (Issue #226, ADR-006). net10.0 target only
SchemaRepository (component removed, internal reserved-id kept). The 7.1.10 fix (#223) recovered the schema for reading constraint values, but a later [FromBody]/[FromForm] operation binding the same type made Swashbuckle emit a $ref to a component that no longer exists (confirmed empirically), and rules applied on the recovered throwaway instance never reached the documentSchemaRepository.ReplaceSchemaId (public API since Swashbuckle 10.1.0) before removing a side-effect schema — the reservation is cleared together with the component, so the next operation binding the same type regenerates a full component and the rules reach the real document object. SwashbuckleSchemaProvider tracks Type → schemaId for every GetSchemaForType call (including the Issue #209 ancestor walk) to make the healing possibleReplaceSchemaId does not exist, and bumping the minimum would force the Microsoft.OpenApi 2.x breaking change on consumers. The throwaway recovery from #223 remains as a safety net on all targetsdocs/adr/ADR-006-shared-dto-state-healing-cleanup.md. Thanks to @jgarciadelanoceda for suggesting the ReplaceSchemaId approach[FromQuery]/[AsParameters] DTO shared by more than one endpoint lost its FluentValidation rules on every endpoint after the first (Issue #223)
FluentValidationOperationFilter runs once per operation. The Issue #180 cleanup removes the temporary container schema from SchemaRepository.Schemas, but Swashbuckle keeps the type in its internal reserved-ids map, which the cleanup does not touch. For the 2nd+ endpoint, GetSchemaForType → GenerateSchema then returns a bare $ref (no Properties) and the schema is no longer in Schemas, so the "has properties" guard was skipped and no rules were applied. Only reproduces with the default RemoveUnusedQuerySchemas = trueSwashbuckleSchemaProvider.GetSchemaForType, when the returned schema has no properties and the id is absent from the shared repository (the reserved-but-removed state), the concrete schema is recovered by generating it into a throwaway SchemaRepository. Fully isolated — it never mutates the shared repository or its reserved-id state — so the Issue #180 cleanup and all other OperationFilter behavior (required marking #209, nested [FromQuery] #211/#213, request bodies, #216) are preservedshort/byte/ushort/uint/ulong/sbyte-typed rules were silently dropped (Issue #222)
IsNumeric in the shared core (MicroElements.OpenApi.FluentValidation) only recognized int/long/float/double/decimal/BigInteger, so a Between/Comparison rule whose bound was a small integer type (e.g. InclusiveBetween((short)1, (short)99)) matched but produced no minimum/maximum — even though the generator emits "type": "integer" for the property. This could not be worked around at the validator definition site because those rule overloads only accept bounds of the property's own typeIsNumeric now recognizes all integer primitives (sbyte/byte/short/ushort/int/uint/long/ulong) in addition to the floating/decimal/BigInteger types; NumericToDecimal already converted them all. One change in the shared core fixes all three providers (Swashbuckle, NSwag, and the native Microsoft.AspNetCore.OpenApi transformer)Swashbuckle.AspNetCore.SwaggerGen on the net10.0 target was bumped 10.0.0 → 10.2.1, which resolves Microsoft.OpenApi to the patched 2.7.5 (was 2.3.0). This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). The net8.0/net9.0 targets use Swashbuckle 8.1.1 → Microsoft.OpenApi v1 and were never in the advisory rangeIFormFile uploads (Issue #216): stable rollup of everything in 7.1.8-beta.1 and 7.1.8-beta.2 below (new File-level rules .FileContentType(), .MaxFileSize(), .MinFileSize(), .FileSizeBetween(); Swashbuckle / NSwag / Microsoft.AspNetCore.OpenApi emit multipart encoding.contentType and description annotations)7.1.8-beta.1 below, plus: Microsoft.AspNetCore.OpenApi now also emits encoding.contentType for the file part (Issue #216) — the FluentValidationOperationTransformer writes requestBody.content["multipart/form-data"].encoding.<part>.contentType so UIs like Scalar/Swagger UI can show the accepted media types, not just the description. Works on net9.0 (inline form schema) and net10.0 (resolves the whole-body $ref component to find the part name)Full release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
[FromQuery] and a request body lost its rules, and the document could contain a dangling $ref (net10.0)When the same DTO was bound as a flattened [FromQuery]/[AsParameters] container by one endpoint and as a request body ([FromBody]/[FromForm]) by another, the per-operation schema cleanup left the type "reserved-but-removed" inside Swashbuckle's SchemaRepository. The body operation then emitted a $ref to a component that no longer exists — an invalid document — and the FluentValidation constraints never reached it. Reproduces with the default RemoveUnusedQuerySchemas = true.
app.MapGet("/search", ([FromQuery] HelloRequest request) => ...); // processed first: cleanup runs
app.MapPost("/hello", ([FromBody] HelloRequest request) => ...); // before: $ref to a missing component, no rules
// after: component regenerated, rules appliedGenerateSchema kept returning a bare $ref without regenerating the component. The 7.1.10 fix (#223) recovered the schema for reading constraint values only; it could not restore the document's component.SchemaRepository.ReplaceSchemaId (public API since Swashbuckle 10.1.0) before removing a side-effect schema — the reservation is cleared together with the component, so the next operation binding the same type regenerates a full component and the rules reach the real document object.ReplaceSchemaId does not exist — they keep the 7.1.10 throwaway-recovery behavior (parameters path). The recovery also remains as a safety net on all targets.Thanks to @jgarciadelanoceda for suggesting the ReplaceSchemaId approach (added in domaindrivendev/Swashbuckle.AspNetCore#3708).
Full changelog: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). Th…
$# Changes in 7.1.10
[FromQuery]/[AsParameters] DTO shared by more than one endpoint lost its FluentValidation rules on every endpoint after the first (Issue #223)
FluentValidationOperationFilter runs once per operation. The Issue #180 cleanup removes the temporary container schema from SchemaRepository.Schemas, but Swashbuckle keeps the type in its internal reserved-ids map, which the cleanup does not touch. For the 2nd+ endpoint, GetSchemaForType → GenerateSchema then returns a bare $ref (no Properties) and the schema is no longer in Schemas, so the "has properties" guard was skipped and no rules were applied. Only reproduces with the default RemoveUnusedQuerySchemas = trueSwashbuckleSchemaProvider.GetSchemaForType, when the returned schema has no properties and the id is absent from the shared repository (the reserved-but-removed state), the concrete schema is recovered by generating it into a throwaway SchemaRepository. Fully isolated — it never mutates the shared repository or its reserved-id state — so the Issue #180 cleanup and all other OperationFilter behavior (required marking #209, nested [FromQuery] #211/#213, request bodies, #216) are preservedshort/byte/ushort/uint/ulong/sbyte-typed rules were silently dropped (Issue #222)
IsNumeric in the shared core (MicroElements.OpenApi.FluentValidation) only recognized int/long/float/double/decimal/BigInteger, so a Between/Comparison rule whose bound was a small integer type (e.g. InclusiveBetween((short)1, (short)99)) matched but produced no minimum/maximum — even though the generator emits "type": "integer" for the property. This could not be worked around at the validator definition site because those rule overloads only accept bounds of the property's own typeIsNumeric now recognizes all integer primitives (sbyte/byte/short/ushort/int/uint/long/ulong) in addition to the floating/decimal/BigInteger types; NumericToDecimal already converted them all. One change in the shared core fixes all three providers (Swashbuckle, NSwag, and the native Microsoft.AspNetCore.OpenApi transformer)Swashbuckle.AspNetCore.SwaggerGen on the net10.0 target was bumped 10.0.0 → 10.2.1, which resolves Microsoft.OpenApi to the patched 2.7.5 (was 2.3.0). This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). The net8.0/net9.0 targets use Swashbuckle 8.1.1 → Microsoft.OpenApi v1 and were never in the advisory rangeIFormFile uploads (Issue #216): stable rollup of everything in 7.1.8-beta.1 and 7.1.8-beta.2 below (new File-level rules .FileContentType(), .MaxFileSize(), .MinFileSize(), .FileSizeBetween(); Swashbuckle / NSwag / Microsoft.AspNetCore.OpenApi emit multipart encoding.contentType and description annotations)7.1.8-beta.1 below, plus: Microsoft.AspNetCore.OpenApi now also emits encoding.contentType for the file part (Issue #216) — the FluentValidationOperationTransformer writes requestBody.content["multipart/form-data"].encoding.<part>.contentType so UIs like Scalar/Swagger UI can show the accepted media types, not just the description. Works on net9.0 (inline form schema) and net10.0 (resolves the whole-body $ref component to find the part name)IFormFile uploads (Issue #216)
MicroElements.OpenApi.FluentValidation (namespace MicroElements.OpenApi.FluentValidation.FileUpload): .FileContentType(params string[]), .MaxFileSize(long), .MinFileSize(long), .FileSizeBetween(long, long) on IRuleBuilder<T, IFormFile>. They both enforce validation at runtime and surface metadata for OpenAPI generationIFormFile members (RuleFor(x => x.File.Length) / RuleFor(x => x.File.ContentType)) are named File.Length / File.ContentType and never match the flat schema property File, so they were silently dropped; and Must(...) is opaque so allowed content types could not be reflected. Use the new File-level rules insteadrequestBody.content["multipart/form-data"].encoding.<part>.contentType (comma-joined allowed types) and appends the allowed types and size limits to the file property description. File size is never emitted as maxLength (which counts characters, not bytes). Works on net8.0/net9.0 (Microsoft.OpenApi v1, OpenAPI 3.0) and net10.0 (Microsoft.OpenApi v2, OpenAPI 3.1)FluentValidationOperationProcessor (IOperationProcessor) emits multipart encoding for file parts; the allowed types and size limits are also appended to the file part description. Register it alongside the schema processor: settings.OperationProcessors.Add(serviceProvider.GetService<FluentValidationOperationProcessor>()). Known NSwag limitation: OpenApiEncoding.EncodingType serializes as encodingType rather than the OpenAPI-spec contentType (through at least NSwag 14.7.x), so the description is the guaranteed-visible carrierdescription, and (since 7.1.8-beta.2) the allowed types are also emitted as encoding.contentType on the multipart media typedescription (annotation only; enforcement stays server-side via FluentValidation)Full release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
[FromQuery] DTO shared by several endpoints lost its rules on every endpoint after the firstWhen the same [FromQuery]/[AsParameters] DTO was bound by more than one endpoint, only the first operation received FluentValidation constraints (minLength, pattern, maximum, ...) — every subsequent endpoint silently lost them. Reproduces with the default RemoveUnusedQuerySchemas = true.
app.MapGet("/hello1", ([FromQuery] HelloRequest request) => ...); // constraints emitted
app.MapGet("/hello2", ([FromQuery] HelloRequest request) => ...); // before: constraints lost — after: emittedFluentValidationOperationFilter runs once per operation. The Issue #180 cleanup removes the temporary [FromQuery] container schema from SchemaRepository.Schemas, but Swashbuckle also keeps the type in its internal reserved-ids map, which the cleanup cannot touch. For the 2nd+ endpoint GenerateSchema then returns a bare $ref with no Properties, so the "has properties" guard skipped all rules.SwashbuckleSchemaProvider.GetSchemaForType detects this reserved-but-removed state and recovers the concrete schema by generating it into a throwaway SchemaRepository. Fully isolated — the shared repository and its reserved-id state are never mutated — so the Issue #180 cleanup and all other operation-filter behavior (required marking #209, nested [FromQuery] #211/#213, request bodies and encoding.contentType #216) are preserved.ReplaceSchemaId-based state-healing cleanup on the net10.0 target (needs Swashbuckle >= 10.1.0).Thanks to @jgarciadelanoceda for the report and the ReplaceSchemaId discussion.
Full changelog: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). Th…
$# Changes in 7.1.9
short/byte/ushort/uint/ulong/sbyte-typed rules were silently dropped (Issue #222)
IsNumeric in the shared core (MicroElements.OpenApi.FluentValidation) only recognized int/long/float/double/decimal/BigInteger, so a Between/Comparison rule whose bound was a small integer type (e.g. InclusiveBetween((short)1, (short)99)) matched but produced no minimum/maximum — even though the generator emits "type": "integer" for the property. This could not be worked around at the validator definition site because those rule overloads only accept bounds of the property's own typeIsNumeric now recognizes all integer primitives (sbyte/byte/short/ushort/int/uint/long/ulong) in addition to the floating/decimal/BigInteger types; NumericToDecimal already converted them all. One change in the shared core fixes all three providers (Swashbuckle, NSwag, and the native Microsoft.AspNetCore.OpenApi transformer)Swashbuckle.AspNetCore.SwaggerGen on the net10.0 target was bumped 10.0.0 → 10.2.1, which resolves Microsoft.OpenApi to the patched 2.7.5 (was 2.3.0). This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). The net8.0/net9.0 targets use Swashbuckle 8.1.1 → Microsoft.OpenApi v1 and were never in the advisory rangeIFormFile uploads (Issue #216): stable rollup of everything in 7.1.8-beta.1 and 7.1.8-beta.2 below (new File-level rules .FileContentType(), .MaxFileSize(), .MinFileSize(), .FileSizeBetween(); Swashbuckle / NSwag / Microsoft.AspNetCore.OpenApi emit multipart encoding.contentType and description annotations)7.1.8-beta.1 below, plus: Microsoft.AspNetCore.OpenApi now also emits encoding.contentType for the file part (Issue #216) — the FluentValidationOperationTransformer writes requestBody.content["multipart/form-data"].encoding.<part>.contentType so UIs like Scalar/Swagger UI can show the accepted media types, not just the description. Works on net9.0 (inline form schema) and net10.0 (resolves the whole-body $ref component to find the part name)IFormFile uploads (Issue #216)
MicroElements.OpenApi.FluentValidation (namespace MicroElements.OpenApi.FluentValidation.FileUpload): .FileContentType(params string[]), .MaxFileSize(long), .MinFileSize(long), .FileSizeBetween(long, long) on IRuleBuilder<T, IFormFile>. They both enforce validation at runtime and surface metadata for OpenAPI generationIFormFile members (RuleFor(x => x.File.Length) / RuleFor(x => x.File.ContentType)) are named File.Length / File.ContentType and never match the flat schema property File, so they were silently dropped; and Must(...) is opaque so allowed content types could not be reflected. Use the new File-level rules insteadrequestBody.content["multipart/form-data"].encoding.<part>.contentType (comma-joined allowed types) and appends the allowed types and size limits to the file property description. File size is never emitted as maxLength (which counts characters, not bytes). Works on net8.0/net9.0 (Microsoft.OpenApi v1, OpenAPI 3.0) and net10.0 (Microsoft.OpenApi v2, OpenAPI 3.1)FluentValidationOperationProcessor (IOperationProcessor) emits multipart encoding for file parts; the allowed types and size limits are also appended to the file part description. Register it alongside the schema processor: settings.OperationProcessors.Add(serviceProvider.GetService<FluentValidationOperationProcessor>()). Known NSwag limitation: OpenApiEncoding.EncodingType serializes as encodingType rather than the OpenAPI-spec contentType (through at least NSwag 14.7.x), so the description is the guaranteed-visible carrierdescription, and (since 7.1.8-beta.2) the allowed types are also emitted as encoding.contentType on the multipart media typedescription (annotation only; enforcement stays server-side via FluentValidation)[FromQuery] fixes (#209 + #211) now also apply to the native Microsoft.AspNetCore.OpenApi transformer and the experimental Swashbuckle DocumentFilter (Issue #213)
FluentValidationOperationTransformer (package MicroElements.AspNetCore.OpenApi.FluentValidation) previously set a nested parameter required from the leaf validator alone — ignoring both whether the SetValidator/ChildRules chain reaches the leaf (#211) and whether every ancestor of the dot-path is required (#209). It now follows the same reachability + ancestor-required rules as the Swashbuckle OperationFilterFluentValidationDocumentFilter no longer copies value constraints onto a flattened nested parameter whose nested validation is not wired from the root validator (#211)GetMethodInfo now resolves the action method from ControllerActionDescriptor (MVC controllers), not only minimal-API endpoint metadata, so the dot-path root type can be resolved for controller actions[FromQuery] parameter flattening)[FromQuery] was reflected in the OpenAPI document even when it was not wired into the root validator via SetValidator/ChildRules (Issue #211)
FluentValidationOperationFilter resolved the leaf container's validator directly from the registry (by ModelMetadata.ContainerType), so a nested NotEmpty() marked the flattened parameter (e.g. RequiredSubType.SubProperty) as required even though FluentValidation never validates an unwired child object — the OpenAPI doc claimed required, but the API accepted requests without itSetValidator/ChildRules chain from the action's root [FromQuery] validator actually reaches the leaf container; otherwise the parameter is left unconstrained, matching runtime behavior[FromQuery] type (only a leaf/child validator is registered), a flattened nested parameter is now left unconstrained — matching runtime, where no validation runs without a root validator[FromQuery] was wrongly marked as a required parameter (Issue #209)
[FromQuery] validation match the leaf property name, but FluentValidationOperationFilter then set required based solely on the leaf type, ignoring whether the ancestor segment of the dot-path was optionalOptionalSubType.SubProperty and RequiredSubType.SubProperty), a NotEmpty() on the leaf marked both flattened parameters as requiredrequired only when every ancestor segment of the dot-path is required — resolved from the action's root [FromQuery] type, combining the native schema required (e.g. the C# required modifier) with FluentValidation NotNull/NotEmpty rulesminLength) still apply to an optional nested parameter when it is providedFull release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
Validation bounds whose value is a small integer type (short/byte/ushort/uint/ulong/sbyte) produced no minimum/maximum in the generated schema. The Between/Comparison rules matched, but IsNumeric did not recognize the boxed value, so the bound was skipped — even though the property is emitted as "type": "integer".
RuleFor(x => x.Quantity).InclusiveBetween((short)1, (short)99);
// before: { "type": "integer", "format": "int16" }
// after: { "type": "integer", "format": "int16", "minimum": 1, "maximum": 99 }IsNumeric in the shared core (MicroElements.OpenApi.FluentValidation) now recognizes all integer primitives — sbyte/byte/short/ushort/int/uint/long/ulong — in addition to float/double/decimal/BigInteger. NumericToDecimal already converted them all.decimal comfortably covers ulong.MaxValue, and BigInteger keeps its dedicated branch.Thanks to @send0xx for the precise report and the proposed fix.
Full changelog: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). Th…
$# Changes in 7.1.8
Swashbuckle.AspNetCore.SwaggerGen on the net10.0 target was bumped 10.0.0 → 10.2.1, which resolves Microsoft.OpenApi to the patched 2.7.5 (was 2.3.0). This clears GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CWE-674 uncontrolled recursion — a circular $ref schema could stack-overflow the OpenAPI reader). The net8.0/net9.0 targets use Swashbuckle 8.1.1 → Microsoft.OpenApi v1 and were never in the advisory rangeIFormFile uploads (Issue #216): stable rollup of everything in 7.1.8-beta.1 and 7.1.8-beta.2 below (new File-level rules .FileContentType(), .MaxFileSize(), .MinFileSize(), .FileSizeBetween(); Swashbuckle / NSwag / Microsoft.AspNetCore.OpenApi emit multipart encoding.contentType and description annotations)7.1.8-beta.1 below, plus: Microsoft.AspNetCore.OpenApi now also emits encoding.contentType for the file part (Issue #216) — the FluentValidationOperationTransformer writes requestBody.content["multipart/form-data"].encoding.<part>.contentType so UIs like Scalar/Swagger UI can show the accepted media types, not just the description. Works on net9.0 (inline form schema) and net10.0 (resolves the whole-body $ref component to find the part name)IFormFile uploads (Issue #216)
MicroElements.OpenApi.FluentValidation (namespace MicroElements.OpenApi.FluentValidation.FileUpload): .FileContentType(params string[]), .MaxFileSize(long), .MinFileSize(long), .FileSizeBetween(long, long) on IRuleBuilder<T, IFormFile>. They both enforce validation at runtime and surface metadata for OpenAPI generationIFormFile members (RuleFor(x => x.File.Length) / RuleFor(x => x.File.ContentType)) are named File.Length / File.ContentType and never match the flat schema property File, so they were silently dropped; and Must(...) is opaque so allowed content types could not be reflected. Use the new File-level rules insteadrequestBody.content["multipart/form-data"].encoding.<part>.contentType (comma-joined allowed types) and appends the allowed types and size limits to the file property description. File size is never emitted as maxLength (which counts characters, not bytes). Works on net8.0/net9.0 (Microsoft.OpenApi v1, OpenAPI 3.0) and net10.0 (Microsoft.OpenApi v2, OpenAPI 3.1)FluentValidationOperationProcessor (IOperationProcessor) emits multipart encoding for file parts; the allowed types and size limits are also appended to the file part description. Register it alongside the schema processor: settings.OperationProcessors.Add(serviceProvider.GetService<FluentValidationOperationProcessor>()). Known NSwag limitation: OpenApiEncoding.EncodingType serializes as encodingType rather than the OpenAPI-spec contentType (through at least NSwag 14.7.x), so the description is the guaranteed-visible carrierdescription, and (since 7.1.8-beta.2) the allowed types are also emitted as encoding.contentType on the multipart media typedescription (annotation only; enforcement stays server-side via FluentValidation)[FromQuery] fixes (#209 + #211) now also apply to the native Microsoft.AspNetCore.OpenApi transformer and the experimental Swashbuckle DocumentFilter (Issue #213)
FluentValidationOperationTransformer (package MicroElements.AspNetCore.OpenApi.FluentValidation) previously set a nested parameter required from the leaf validator alone — ignoring both whether the SetValidator/ChildRules chain reaches the leaf (#211) and whether every ancestor of the dot-path is required (#209). It now follows the same reachability + ancestor-required rules as the Swashbuckle OperationFilterFluentValidationDocumentFilter no longer copies value constraints onto a flattened nested parameter whose nested validation is not wired from the root validator (#211)GetMethodInfo now resolves the action method from ControllerActionDescriptor (MVC controllers), not only minimal-API endpoint metadata, so the dot-path root type can be resolved for controller actions[FromQuery] parameter flattening)[FromQuery] was reflected in the OpenAPI document even when it was not wired into the root validator via SetValidator/ChildRules (Issue #211)
FluentValidationOperationFilter resolved the leaf container's validator directly from the registry (by ModelMetadata.ContainerType), so a nested NotEmpty() marked the flattened parameter (e.g. RequiredSubType.SubProperty) as required even though FluentValidation never validates an unwired child object — the OpenAPI doc claimed required, but the API accepted requests without itSetValidator/ChildRules chain from the action's root [FromQuery] validator actually reaches the leaf container; otherwise the parameter is left unconstrained, matching runtime behavior[FromQuery] type (only a leaf/child validator is registered), a flattened nested parameter is now left unconstrained — matching runtime, where no validation runs without a root validator[FromQuery] was wrongly marked as a required parameter (Issue #209)
[FromQuery] validation match the leaf property name, but FluentValidationOperationFilter then set required based solely on the leaf type, ignoring whether the ancestor segment of the dot-path was optionalOptionalSubType.SubProperty and RequiredSubType.SubProperty), a NotEmpty() on the leaf marked both flattened parameters as requiredrequired only when every ancestor segment of the dot-path is required — resolved from the action's root [FromQuery] type, combining the native schema required (e.g. the C# required modifier) with FluentValidation NotNull/NotEmpty rulesminLength) still apply to an optional nested parameter when it is provided$ref still replaced with an inline copy (and the child component left orphaned) when nested object constraints come from ChildRules or an inline child validator (Issue #198, comment 4601720562)
$refs, but when the nested type had no standalone validator its component schema gained its Required only after the parent's inline snapshot, so the stale Required diverged and defeated the restore check — leaving an inline copy and an orphaned componentRequired comparison in HasValidationConstraintChanges is now directional — restoration is only blocked when the inline copy carries a required entry the component lacksSetValidator (with a standalone child validator) was already correct; BigInteger/enum per-model constraints (Issues #146/#176) continue to workConditionalRulesMode option to control how .When()/.Unless() conditional rules are handled during schema generation (Issue #203)
Exclude (default): conditional rules are excluded from the schema (backward-compatible, existing behavior)Include: conditional rules are included in the schema (useful when .When() is a null-guard and constraints should still appear)IncludeWithWarning: same as Include but logs a warning for each conditional rule includedoptions.ConditionalRules = ConditionalRulesMode.Include;.Matches() rules on one property displayed incorrectly — only the first pattern shown, property duplicated (Issue #204)
allOf subschemas, which Swagger UI/Redoc/Scalar collapse, keeping only the first pattern.Matches() rules are combined into a single pattern via lookahead assertions (e.g. (?=[\s\S]*(?:[a-z]))(?=[\s\S]*(?:[A-Z]))), preserving .Matches() semantics and rendering correctlyMicroElements.AspNetCore.OpenApi.FluentValidation, and NSwag (NSwag previously kept only the last pattern)SchemaGenerationOptions.UseAllOfForMultipleRules default true → false; set it to true to keep the legacy allOf representationFull release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
Closes the transitive high-severity advisory GHSA-v5pm-xwqc-g5wc / CVE-2026-49451 (CVSS 7.5, CWE-674 Uncontrolled Recursion — a circular $ref schema could stack-overflow the OpenAPI reader; availability / process-termination only).
Swashbuckle.AspNetCore.SwaggerGen 10.0.0 → 10.2.1 on the net10.0 target, which resolves the transitive Microsoft.OpenApi from the vulnerable 2.3.0 to the patched 2.7.5.8.1.1 → Microsoft.OpenApi v1 and were never in the advisory range — left unchanged.Stable rollup of the work previously shipped as 7.1.8-beta.1 / 7.1.8-beta.2:
MicroElements.OpenApi.FluentValidation.FileUpload: .FileContentType(params string[]), .MaxFileSize(long), .MinFileSize(long), .FileSizeBetween(long, long) on IRuleBuilder<T, IFormFile>.multipart/form-data encoding.contentType for file parts and append the allowed types / size limits to the file property description.Full changelog: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
Fixed: Replaced deprecated PackageLicenseUrl with PackageLicenseExpression (Issue #144)
$# Changes in 7.1.7
[FromQuery] fixes (#209 + #211) now also apply to the native Microsoft.AspNetCore.OpenApi transformer and the experimental Swashbuckle DocumentFilter (Issue #213)
FluentValidationOperationTransformer (package MicroElements.AspNetCore.OpenApi.FluentValidation) previously set a nested parameter required from the leaf validator alone — ignoring both whether the SetValidator/ChildRules chain reaches the leaf (#211) and whether every ancestor of the dot-path is required (#209). It now follows the same reachability + ancestor-required rules as the Swashbuckle OperationFilterFluentValidationDocumentFilter no longer copies value constraints onto a flattened nested parameter whose nested validation is not wired from the root validator (#211)GetMethodInfo now resolves the action method from ControllerActionDescriptor (MVC controllers), not only minimal-API endpoint metadata, so the dot-path root type can be resolved for controller actions[FromQuery] parameter flattening)[FromQuery] was reflected in the OpenAPI document even when it was not wired into the root validator via SetValidator/ChildRules (Issue #211)
FluentValidationOperationFilter resolved the leaf container's validator directly from the registry (by ModelMetadata.ContainerType), so a nested NotEmpty() marked the flattened parameter (e.g. RequiredSubType.SubProperty) as required even though FluentValidation never validates an unwired child object — the OpenAPI doc claimed required, but the API accepted requests without itSetValidator/ChildRules chain from the action's root [FromQuery] validator actually reaches the leaf container; otherwise the parameter is left unconstrained, matching runtime behavior[FromQuery] type (only a leaf/child validator is registered), a flattened nested parameter is now left unconstrained — matching runtime, where no validation runs without a root validator[FromQuery] was wrongly marked as a required parameter (Issue #209)
[FromQuery] validation match the leaf property name, but FluentValidationOperationFilter then set required based solely on the leaf type, ignoring whether the ancestor segment of the dot-path was optionalOptionalSubType.SubProperty and RequiredSubType.SubProperty), a NotEmpty() on the leaf marked both flattened parameters as requiredrequired only when every ancestor segment of the dot-path is required — resolved from the action's root [FromQuery] type, combining the native schema required (e.g. the C# required modifier) with FluentValidation NotNull/NotEmpty rulesminLength) still apply to an optional nested parameter when it is provided$ref still replaced with an inline copy (and the child component left orphaned) when nested object constraints come from ChildRules or an inline child validator (Issue #198, comment 4601720562)
$refs, but when the nested type had no standalone validator its component schema gained its Required only after the parent's inline snapshot, so the stale Required diverged and defeated the restore check — leaving an inline copy and an orphaned componentRequired comparison in HasValidationConstraintChanges is now directional — restoration is only blocked when the inline copy carries a required entry the component lacksSetValidator (with a standalone child validator) was already correct; BigInteger/enum per-model constraints (Issues #146/#176) continue to workConditionalRulesMode option to control how .When()/.Unless() conditional rules are handled during schema generation (Issue #203)
Exclude (default): conditional rules are excluded from the schema (backward-compatible, existing behavior)Include: conditional rules are included in the schema (useful when .When() is a null-guard and constraints should still appear)IncludeWithWarning: same as Include but logs a warning for each conditional rule includedoptions.ConditionalRules = ConditionalRulesMode.Include;.Matches() rules on one property displayed incorrectly — only the first pattern shown, property duplicated (Issue #204)
allOf subschemas, which Swagger UI/Redoc/Scalar collapse, keeping only the first pattern.Matches() rules are combined into a single pattern via lookahead assertions (e.g. (?=[\s\S]*(?:[a-z]))(?=[\s\S]*(?:[A-Z]))), preserving .Matches() semantics and rendering correctlyMicroElements.AspNetCore.OpenApi.FluentValidation, and NSwag (NSwag previously kept only the last pattern)SchemaGenerationOptions.UseAllOfForMultipleRules default true → false; set it to true to keep the legacy allOf representationFluentValidationOperationTransformer (IOpenApiOperationTransformer) for MicroElements.AspNetCore.OpenApi.FluentValidation (Issue #200)
[AsParameters] now receive validation constraints (min/max, required, pattern, etc.)[AsParameters]AddFluentValidationRules()FluentValidationSchemaTransformer skipped all property-level schemas, but for nested object types this was the only transformer call$ref replaced with inline schema copy when using SetValidator with nested object types (Issue #198)
ResolveRefProperty (introduced in 7.1.2 for BigInteger isolation) replaced all $ref properties with copies, destroying reference structure in the OpenAPI document$ref properties before rule application, restore them afterwards if no validation constraints were added by rulesBigInteger support for min/max validation constraints in OpenAPI schema generation (Issue #146)
IsNumeric() and NumericToDecimal() now handle BigInteger valuesBigInteger properties with GreaterThan, LessThan, InclusiveBetween, ExclusiveBetween rules produce correct minimum/maximum in SwaggerBigInteger supportBigInteger values (exceeding decimal range) are handled gracefully via existing try/catchBigInteger type with different constraints (net10.0)
ResolveRefProperty creates an isolated shallow copy before applying rule mutations$ref-based schema corruption across models in SchemaRepositoryPackageLicenseUrl with PackageLicenseExpression (Issue #144)PackageIconUrl with embedded PackageIconFull release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
Stable release 7.1.7 — promotes 7.1.7-beta.3 to stable.
[FromQuery] fixes (#209 + #211) now also apply to the native Microsoft.AspNetCore.OpenApi transformer and the experimental Swashbuckle DocumentFilter (Issue #213)
FluentValidationOperationTransformer now follows the same reachability + ancestor-required rules as the Swashbuckle OperationFilterFluentValidationDocumentFilter no longer copies value constraints onto a flattened nested parameter whose nested validation is not wired from the root validatorGetMethodInfo now resolves the action method from ControllerActionDescriptor (MVC controllers), not only minimal-API endpoint metadata[FromQuery] parameter flattening)[FromQuery] was reflected in the OpenAPI document even when it was not wired into the root validator via SetValidator/ChildRules (Issue #211)
SetValidator/ChildRules chain from the action's root [FromQuery] validator actually reaches the leaf container[FromQuery] type, a flattened nested parameter is left unconstrained — matching runtime[FromQuery] was wrongly marked as a required parameter (Issue #209)
required only when every ancestor segment of the dot-path is requiredminLength) still apply to an optional nested parameter when it is providedFull changelog: CHANGELOG.md
Fixed: Replaced deprecated PackageLicenseUrl with PackageLicenseExpression (Issue #144)
$# Changes in 7.1.6
$ref still replaced with an inline copy (and the child component left orphaned) when nested object constraints come from ChildRules or an inline child validator (Issue #198, comment 4601720562)
$refs, but when the nested type had no standalone validator its component schema gained its Required only after the parent's inline snapshot, so the stale Required diverged and defeated the restore check — leaving an inline copy and an orphaned componentRequired comparison in HasValidationConstraintChanges is now directional — restoration is only blocked when the inline copy carries a required entry the component lacksSetValidator (with a standalone child validator) was already correct; BigInteger/enum per-model constraints (Issues #146/#176) continue to workConditionalRulesMode option to control how .When()/.Unless() conditional rules are handled during schema generation (Issue #203)
Exclude (default): conditional rules are excluded from the schema (backward-compatible, existing behavior)Include: conditional rules are included in the schema (useful when .When() is a null-guard and constraints should still appear)IncludeWithWarning: same as Include but logs a warning for each conditional rule includedoptions.ConditionalRules = ConditionalRulesMode.Include;.Matches() rules on one property displayed incorrectly — only the first pattern shown, property duplicated (Issue #204)
allOf subschemas, which Swagger UI/Redoc/Scalar collapse, keeping only the first pattern.Matches() rules are combined into a single pattern via lookahead assertions (e.g. (?=[\s\S]*(?:[a-z]))(?=[\s\S]*(?:[A-Z]))), preserving .Matches() semantics and rendering correctlyMicroElements.AspNetCore.OpenApi.FluentValidation, and NSwag (NSwag previously kept only the last pattern)SchemaGenerationOptions.UseAllOfForMultipleRules default true → false; set it to true to keep the legacy allOf representationFluentValidationOperationTransformer (IOpenApiOperationTransformer) for MicroElements.AspNetCore.OpenApi.FluentValidation (Issue #200)
[AsParameters] now receive validation constraints (min/max, required, pattern, etc.)[AsParameters]AddFluentValidationRules()FluentValidationSchemaTransformer skipped all property-level schemas, but for nested object types this was the only transformer call$ref replaced with inline schema copy when using SetValidator with nested object types (Issue #198)
ResolveRefProperty (introduced in 7.1.2 for BigInteger isolation) replaced all $ref properties with copies, destroying reference structure in the OpenAPI document$ref properties before rule application, restore them afterwards if no validation constraints were added by rulesBigInteger support for min/max validation constraints in OpenAPI schema generation (Issue #146)
IsNumeric() and NumericToDecimal() now handle BigInteger valuesBigInteger properties with GreaterThan, LessThan, InclusiveBetween, ExclusiveBetween rules produce correct minimum/maximum in SwaggerBigInteger supportBigInteger values (exceeding decimal range) are handled gracefully via existing try/catchBigInteger type with different constraints (net10.0)
ResolveRefProperty creates an isolated shallow copy before applying rule mutations$ref-based schema corruption across models in SchemaRepositoryPackageLicenseUrl with PackageLicenseExpression (Issue #144)PackageIconUrl with embedded PackageIcon[FromQuery] parameters (Issue #162)
[FromQuery] models with nested objects into flat parameters (e.g., operation.op), the full dot-path name was used for schema property matching instead of the leaf name (op)EqualsIgnoreAll("operation.op", "op") compared "OPERATIONOP" vs "OP" and failed to matchLastIndexOf('.') in both FluentValidationOperationFilter and FluentValidationDocumentFiltera.b.c → c)SetNotNullableIfMinimumGreaterThenZero option to separately control nullable behavior for numeric Minimum constraints (Issue #154, ported from vchirikov fork PR #2)
SetNotNullableIfMinLengthGreaterThenZero (for string MinLength)false (backward compatible)SetNotNullableIfMinLengthGreaterThenZero option now works in NSwag provider (Issue #154)
NSwagFluentValidationRuleProvider now accepts IOptions<SchemaGenerationOptions>SetNotNullableIfMinimumGreaterThenZero() which checks actual Minimum value instead of unconditionally setting not-nullableFull release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
Fixed: Replaced deprecated PackageLicenseUrl with PackageLicenseExpression (Issue #144)
$# Changes in 7.1.5
ConditionalRulesMode option to control how .When()/.Unless() conditional rules are handled during schema generation (Issue #203)
Exclude (default): conditional rules are excluded from the schema (backward-compatible, existing behavior)Include: conditional rules are included in the schema (useful when .When() is a null-guard and constraints should still appear)IncludeWithWarning: same as Include but logs a warning for each conditional rule includedoptions.ConditionalRules = ConditionalRulesMode.Include;FluentValidationOperationTransformer (IOpenApiOperationTransformer) for MicroElements.AspNetCore.OpenApi.FluentValidation (Issue #200)
[AsParameters] now receive validation constraints (min/max, required, pattern, etc.)[AsParameters]AddFluentValidationRules()FluentValidationSchemaTransformer skipped all property-level schemas, but for nested object types this was the only transformer call$ref replaced with inline schema copy when using SetValidator with nested object types (Issue #198)
ResolveRefProperty (introduced in 7.1.2 for BigInteger isolation) replaced all $ref properties with copies, destroying reference structure in the OpenAPI document$ref properties before rule application, restore them afterwards if no validation constraints were added by rulesBigInteger support for min/max validation constraints in OpenAPI schema generation (Issue #146)
IsNumeric() and NumericToDecimal() now handle BigInteger valuesBigInteger properties with GreaterThan, LessThan, InclusiveBetween, ExclusiveBetween rules produce correct minimum/maximum in SwaggerBigInteger supportBigInteger values (exceeding decimal range) are handled gracefully via existing try/catchBigInteger type with different constraints (net10.0)
ResolveRefProperty creates an isolated shallow copy before applying rule mutations$ref-based schema corruption across models in SchemaRepositoryPackageLicenseUrl with PackageLicenseExpression (Issue #144)PackageIconUrl with embedded PackageIcon[FromQuery] parameters (Issue #162)
[FromQuery] models with nested objects into flat parameters (e.g., operation.op), the full dot-path name was used for schema property matching instead of the leaf name (op)EqualsIgnoreAll("operation.op", "op") compared "OPERATIONOP" vs "OP" and failed to matchLastIndexOf('.') in both FluentValidationOperationFilter and FluentValidationDocumentFiltera.b.c → c)SetNotNullableIfMinimumGreaterThenZero option to separately control nullable behavior for numeric Minimum constraints (Issue #154, ported from vchirikov fork PR #2)
SetNotNullableIfMinLengthGreaterThenZero (for string MinLength)false (backward compatible)SetNotNullableIfMinLengthGreaterThenZero option now works in NSwag provider (Issue #154)
NSwagFluentValidationRuleProvider now accepts IOptions<SchemaGenerationOptions>SetNotNullableIfMinimumGreaterThenZero() which checks actual Minimum value instead of unconditionally setting not-nullableFull release notes can be found at: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation/blob/master/CHANGELOG.md
Added: FluentValidationOperationTransformer (IOpenApiOperationTransformer) for MicroElements.AspNetCore.OpenApi.FluentValidation (Issue #200)
FluentValidationOperationTransformer (IOpenApiOperationTransformer) for MicroElements.AspNetCore.OpenApi.FluentValidation (Issue #200)
[AsParameters] now receive validation constraints (min/max, required, pattern, etc.)[AsParameters]AddFluentValidationRules()FluentValidationSchemaTransformer skipped all property-level schemas, but for nested object types this was the only transformer callNothing published for this version
Fixed: $ref replaced with inline schema copy when using SetValidator with nested object types (Issue #198)
$ref replaced with inline schema copy when using SetValidator with nested object types (Issue #198)
ResolveRefProperty (introduced in 7.1.2 for BigInteger isolation) replaced all $ref properties with copies, destroying reference structure in the OpenAPI document$ref properties before rule application, restore them afterwards if no validation constraints were added by rulesFixed: Replaced deprecated PackageLicenseUrl with PackageLicenseExpression (Issue #144)
BigInteger support for min/max validation constraints in OpenAPI schema generation (Issue #146)
IsNumeric() and NumericToDecimal() now handle BigInteger valuesBigInteger properties with GreaterThan, LessThan, InclusiveBetween, ExclusiveBetween rules produce correct minimum/maximum in SwaggerBigInteger supportBigInteger values (exceeding decimal range) are handled gracefully via existing try/catchBigInteger type with different constraints (net10.0)
ResolveRefProperty creates an isolated shallow copy before applying rule mutations$ref-based schema corruption across models in SchemaRepositoryPackageLicenseUrl with PackageLicenseExpression (Issue #144)PackageIconUrl with embedded PackageIconFixed: Nested object validation not applied for [FromQuery] parameters (Issue #162)
[FromQuery] parameters (Issue #162)
[FromQuery] models with nested objects into flat parameters (e.g., operation.op), the full dot-path name was used for schema property matching instead of the leaf name (op)EqualsIgnoreAll("operation.op", "op") compared "OPERATIONOP" vs "OP" and failed to matchLastIndexOf('.') in both FluentValidationOperationFilter and FluentValidationDocumentFiltera.b.c → c)SetNotNullableIfMinimumGreaterThenZero option to separately control nullable behavior for numeric Minimum constraints (Issue #154, ported from vchirikov fork PR #2)
SetNotNullableIfMinLengthGreaterThenZero (for string MinLength)false (backward compatible)SetNotNullableIfMinLengthGreaterThenZero option now works in NSwag provider (Issue #154)
NSwagFluentValidationRuleProvider now accepts IOptions<SchemaGenerationOptions>SetNotNullableIfMinimumGreaterThenZero() which checks actual Minimum value instead of unconditionally setting not-nullableAdded: New package MicroElements.AspNetCore.OpenApi.FluentValidation for Microsoft.AspNetCore.OpenApi support (Issue #149)
MicroElements.AspNetCore.OpenApi.FluentValidation for Microsoft.AspNetCore.OpenApi support (Issue #149)
IOpenApiSchemaTransformer for .NET 9 and .NET 10services.AddFluentValidationRulesToOpenApi() + options.AddFluentValidationRules()GetOrCreateSchemaAsyncSampleAspNetCoreOpenApi demonstrating Microsoft.AspNetCore.OpenApi integrationFixed: [AsParameters] validation rules not applied on .NET 8 Minimal APIs (Issue #180)
[AsParameters] validation rules not applied on .NET 8 Minimal APIs (Issue #180)
ModelMetadata.ContainerType is null for [AsParameters] decomposed parametersAsParametersHelper fallback that resolves the container type via [AsParameters] reflection on MethodInfoFluentValidationOperationFilter and FluentValidationDocumentFilterContainerType is already populatedAdded: RemoveUnusedQuerySchemas option (default: true) to control cleanup of container type schemas for [FromQuery]/[AsParameters] types (Issue #180)
RemoveUnusedQuerySchemas option (default: true) to control cleanup of
container type schemas for [FromQuery]/[AsParameters] types (Issue #180)Removed: Deprecated FluentValidation.AspNetCore package reference (Issue #164)
[AsParameters] types in minimal API and [FromQuery] container types create unused schemas in components/schemas (Issue #180)AddKeyedScoped, AddKeyedTransient, AddKeyedSingleton are now discovered automaticallyFluentValidation.AspNetCore package reference (Issue #164)
FluentValidation.DependencyInjectionExtensions 12.0.0Fixed: NullReferenceException when models contain nested object properties (Issue #176 extended)
OpenApiSchemaReference for nested class properties in OpenApiRuleContextTryGetValue check in NSwagRuleContextFixed: InvalidCastException when models contain enum properties (Issue #176)
OpenApiSchemaReference instead of OpenApiSchemaGetProperties() method to avoid cast exceptionFixed: FluentValidation rules not applied to [FromForm] parameters (Issue #170)
[FromForm] parameters (Issue #170)
RequestBody processing in FluentValidationOperationFilter for multipart/form-data and application/x-www-form-urlencoded content typesNothing published for this version
Nothing published for this version
Added support for .NET 8 and .NET 9 to MicroElements.Swashbuckle.FluentValidation.AspNetCore
- see changelog for betas
Change: ILengthValidator support for arrays. Sets MinItems, MaxItems (PR#108 by biggik)
* Supported FluentValidation 11
Sets min compatibility to Swashbuckle.AspNetCore 6.3.0. (PR#102 by guimabdo)
Adding additional fields (Enum, Description) for overridden schema in FluentValidationOperationFilter. (PR#95 by kritsda-jiwatrakan)
Fixed Issue #94: Rule with overridden property name unexpectedly applied to property
Fixed case with many rules for one property. Issue #92
Use new registration method AddFluentValidationRulesToSwagger instead of AddFluentValidationRules to allow all feature set
FluentValidation updated to 10.0.0
Fixed #79: Adding a simple Length validation to a string field should not make the field non-nullable
Swashbuckle.AspNetCore version supports up to 7 (PR#75 by fabich)
RuleForEach supported. Issue #66
FluentValidation updated to [9.0.0]
FluentValidation fix version to [8.3.0, 9)
Mark required properties as not nullable (PR#58 by @manne) Fixes: #55, #57
Swashbuckle.AspNetCore updated to version >= 5.2.0
Supports Swashbuckle 5, net core 3 and brand new System.Text.Json
Supports Swashbuckle 5, net core 3 and brand new System.Text.Json
Swashbuckle.AspNetCore updated to version >= 5.0.0 (new Microsoft.OpenApi)
FluentValidation updated to version >= 8.3
FluentValidation property rules of type CollectionValidationRules (RuleForEach()) are no longer exposed #49.
New IgnoreAllStringComparer was invented to solve problem with different property name formatting: camelCase, PascalCase, snake_case, kebab-case
Added NewtonsoftJsonNamingPolicy example to override property name formatting in new System.Text.Json according Newtonsoft.Json.Serialization.NamingStrategy (see: SampleWebApi)
Fixed invalid documentation on validation rules containing a condition #38
Fixed: #37 (FluentValidationOperationFilter now uses swachbuckle interface to determine json settings)
Nothing published for this version
Nothing published for this version
Added HttpContextServiceProviderValidatorFactory to resolve scoped Dependency Injection (PR#34) by @WarpSpideR
Fixed MinLength rewrite by MaxLength validator #32
Changes: Allow to use SwaggerGenOptions.CustomSchemaIds (PR#31) by @mkjeff
Fixed: #24: NullReferenceException on apply rule for operations.
Breaking Changes: FluentValidation updated to 8.1.3 to support when/unless (PR#27) by @emilssonn
Added: Numeric types includes decimal
Swashbuckle.AspNetCore version locked to versions [1.1.0-3.0.0] because version 4.0.0 has breaking changes. Next version will be 2.0.0 according semve…
Added ScopedSwaggerMiddleware to resolve error "Cannot resolve 'MyValidator' from root provider because it requires scoped service 'TDependency'"
Fixed: #13: Fixed warning with null schema.Properties
Fixed: #12: Fixed NullReferenceException, if schema.Properties is null
New feature: FluentValidation rules for get operation parameters binded from models with validators. Adds swagger validation for parameters: Required,
Improved stability and diagnostics
Fixed: #6: Removed empty required array from swagger schema
Supported float and double values for IComparisonValidator and IBetweenValidator
Refactored to easy add new rules
FluentValidationRulesRegistrator moved to main swagger namespace
Your coding agent can read these notes before it upgrades. Set up the MCP server →