Skip to content

feat: expose FluentValidation rules in API definitions - #26112

Merged
maliming merged 10 commits into
abpframework:devfrom
nazem0:dev
Aug 31, 2026
Merged

maliming merged 10 commits into
abpframework:devfrom
nazem0:dev

Conversation

@nazem0

@nazem0 nazem0 commented Aug 28, 2026 •

Copy link
Copy Markdown
Contributor

Description

Resolves #26083

Adds an extension point for contributing property metadata during API description model generation and provides a FluentValidation implementation.

Changes:

  • Added IPropertyApiDescriptionModelContributor to Volo.Abp.Http.Modeling.
  • Updated AspNetCoreApiDescriptionModelProvider to resolve and execute registered property API description model contributors.
  • Added FluentValidationApiDescriptionModelContributor.
  • Registered the FluentValidation contributor with dependency injection.
  • FluentValidation rules can now contribute validation metadata to API description properties.

This is not a breaking change.

Checklist

  • I fully tested it as developer / designer and created unit / integration tests
  • I documented it (or no need to document or I will create a separate documentation issue)

How to test it?

  1. Create a DTO with a FluentValidation validator.
  2. Define validation rules for its properties.
  3. Register the validator normally with FluentValidation.
  4. Generate the ABP API definition using /api/abp/api-definition.
  5. Verify that the corresponding property API description contains the validation metadata contributed by the FluentValidation contributor.

@nazem0

nazem0 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

@maliming This PR addresses #26083. Would appreciate your feedback on the approach when you have a chance.

The RangeAttribute supports exclusive bounds since .NET 8, but they were lost
in the api definition. The bounds are also written with the invariant culture
now, so the api definition does not depend on the culture of the request.
IPropertyApiDescriptionModelContributor lets a package enrich the properties of
a type after they are created from the attributes. The contributors are applied
by AspNetCoreApiDescriptionModelProvider, so TypeApiDescriptionModel stays a
plain model.
It reflects the statically evaluable FluentValidation rules of a DTO into the
api definition, so the client proxy generators see the same constraints the
attributes already provide. A rule is only published when it applies to every
instance of the type, which leaves out the conditional rules, the rules of a
non-default rule set and the rules of a RuleForEach.

Volo.Abp.FluentValidation keeps its own dependencies, it does not depend on
Volo.Abp.Http.
@maliming
maliming self-requested a review August 29, 2026 09:52
@maliming

Copy link
Copy Markdown
Member

Hi,

Thanks for the PR. I pushed some changes to your branch:

  • The implementation moved to a new Volo.Abp.Http.FluentValidation package, so Volo.Abp.FluentValidation does not depend on Volo.Abp.Http.
  • The contributor takes a context and is async now, like the other contributors in the framework.
  • Only the rules that apply to every instance are published. A When(...) block keeps its condition on the rule instead of the components, and a named rule set never runs with the default selector.
  • GreaterThan and ExclusiveBetween needed the new MinimumIsExclusive / MaximumIsExclusive flags, otherwise the client would accept a boundary value the server rejects.

There is a test project and a documentation section listing what the mapping can not express.

Please take a look.

Thanks

@maliming maliming added this to the 10.8-preview milestone Aug 29, 2026
@nazem0

nazem0 commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Hi @maliming,

Thanks for improving the implementation and adding the tests and documentation.

  • Making a new project, Volo.Abp.Http.FluentValidation, was the most suitable approach.
  • Making the contributor async also makes sense, since we don't know what future contributor implementations might need to do, and this covers any async operations they may require. ✅
  • GreaterThan and ExclusiveBetween — I think I missed that part during the implementation 😐, Thanks for adding it 😅

Range(Type, string, string) keeps its limits as strings until the first
validation, so a numeric limit was reported in the culture that declared it
and could not be merged with a FluentValidation bound.
A magnitude below the decimal range, like 1e-30, parses to a zero, so a round
trip through decimal turned it into a bound the server does not accept.
Range(Type, string, string) keeps its limits as strings until the first
validation. They are converted the way the attribute converts them, so the
reported limit is the one the server validates against whatever the culture
of the request is, and a magnitude outside the decimal range survives it.
The contribution context carries the property now, so a rule on another type,
the ordinal comparison of two strings for example, no longer publishes a
numeric bound. Two bounds are compared as decimals, which are exact for every
integral type, and only fall back to double for the magnitudes decimal can
not hold.
A native integer is a number in the api type system too, and a between rule
that carries its own comparer is skipped when its bounds no longer read as an
interval, because the comparer itself is not on the descriptor.
@maliming
maliming merged commit b2ffa7a into abpframework:dev Aug 31, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support FluentValidation rules in ABP API Definition metadata

2 participants