Follow-up split out from the review of #224 (fix for #223): #224 (comment)
Limitation
The #223 fix recovers a concrete schema for a "reserved-but-removed" [FromQuery] container type by generating it into a throwaway SchemaRepository (SwashbuckleSchemaProvider.GetSchemaForType). That is safe for the parameters path, because there the recovered container schema is only used to read constraint values, which are then copied field-by-field onto the real operationParameter.Schema (FluentValidationOperationFilter.ApplyRulesToParameters).
The request-body path is different. In FluentValidationOperationFilter.ApplyRulesToRequestBody (around FluentValidationOperationFilter.cs:464), when schemaRefId != null it does:
resolvedSchema = schemaProvider.GetSchemaForType(parameterType);
...
FluentValidationSchemaBuilder.ApplyRulesToSchema(schema: resolvedSchema, ...); // mutates resolvedSchema in place
Here the recovered schema is the mutation target — there is no copy-back onto a separate document object. If the recovered schema came from the throwaway fallback, any rules applied to it are written to a disposable object and never reach the emitted document.
When this can bite
Only when a single DTO type is used both:
with RemoveUnusedQuerySchemas = true, and with the query operation processed before the body operation (so the reserved-but-removed state is in effect when the body operation runs).
Not a regression
This path already skipped in this situation before #224 — the empty-Properties guard (resolvedSchema.Properties.Count == 0) short-circuited it. #224 did not change body-bound behavior; it deliberately scoped itself to the "container schema used only for reading constraints" pattern (parameters). So this is a pre-existing gap, surfaced by the review, not something #224 introduced.
Needs a concrete repro to confirm how realistic the mixed [FromQuery] + [FromBody] shared-type scenario is in practice.
Fix direction
The structural fix is "Variant B" from the #223 discussion: make FluentValidationDocumentFilter production-ready and promote it (it is currently Experimental, behind RegistrationOptions.ExperimentalUseDocumentFilter). A document filter batches all types/parameters across the whole document and performs the Issue #180 cleanup once at the end, so the per-operation reserved-but-removed state never arises for any binding source — bodies included. That closes this class of bug uniformly instead of per-path.
Its parity checklist (from the #223 notes): required marking (#209), request bodies + [FromForm] + encoding.contentType (#216), full component-schema coverage (response DTOs, nested SetValidator), and robustness fixes (FindParam only inspects the first operation of a path; duplicate schemasForParameters; nullability/obsolete cleanup).
Status
Filing to track; no action pending on #224 (which stays scoped to #223). Would like feedback on whether the mixed body+query shared-DTO scenario is worth prioritizing on its own, or folded into the Variant B effort.
Follow-up split out from the review of #224 (fix for #223): #224 (comment)
Limitation
The #223 fix recovers a concrete schema for a "reserved-but-removed"
[FromQuery]container type by generating it into a throwawaySchemaRepository(SwashbuckleSchemaProvider.GetSchemaForType). That is safe for the parameters path, because there the recovered container schema is only used to read constraint values, which are then copied field-by-field onto the realoperationParameter.Schema(FluentValidationOperationFilter.ApplyRulesToParameters).The request-body path is different. In
FluentValidationOperationFilter.ApplyRulesToRequestBody(aroundFluentValidationOperationFilter.cs:464), whenschemaRefId != nullit does:Here the recovered schema is the mutation target — there is no copy-back onto a separate document object. If the recovered schema came from the throwaway fallback, any rules applied to it are written to a disposable object and never reach the emitted document.
When this can bite
Only when a single DTO type is used both:
[FromQuery]/[AsParameters]container (whose component is removed by the Issue Validators for records used with [AsParameters] create unused component schemas #180 cleanup, leaving the type reserved-but-removed), and[FromBody]/[FromForm]body in the same document,with
RemoveUnusedQuerySchemas = true, and with the query operation processed before the body operation (so the reserved-but-removed state is in effect when the body operation runs).Not a regression
This path already skipped in this situation before #224 — the empty-
Propertiesguard (resolvedSchema.Properties.Count == 0) short-circuited it. #224 did not change body-bound behavior; it deliberately scoped itself to the "container schema used only for reading constraints" pattern (parameters). So this is a pre-existing gap, surfaced by the review, not something #224 introduced.Needs a concrete repro to confirm how realistic the mixed
[FromQuery]+[FromBody]shared-type scenario is in practice.Fix direction
The structural fix is "Variant B" from the #223 discussion: make
FluentValidationDocumentFilterproduction-ready and promote it (it is currentlyExperimental, behindRegistrationOptions.ExperimentalUseDocumentFilter). A document filter batches all types/parameters across the whole document and performs the Issue #180 cleanup once at the end, so the per-operation reserved-but-removed state never arises for any binding source — bodies included. That closes this class of bug uniformly instead of per-path.Its parity checklist (from the #223 notes): required marking (#209), request bodies +
[FromForm]+encoding.contentType(#216), full component-schema coverage (response DTOs, nestedSetValidator), and robustness fixes (FindParamonly inspects the first operation of a path; duplicateschemasForParameters; nullability/obsolete cleanup).Status
Filing to track; no action pending on #224 (which stays scoped to #223). Would like feedback on whether the mixed body+query shared-DTO scenario is worth prioritizing on its own, or folded into the Variant B effort.