Class InputOriginAttribute
Overrides where validation failures that reach this endpoint without a location of their own are said to have come from.
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InputOriginAttribute : Attribute
- Inheritance
-
InputOriginAttribute
- Inherited Members
- Extension Methods
Examples
// Only needed because this endpoint's residual is not what its binding map implies.
[HttpPost("{id:AccountId}/reconcile")]
[InputOrigin(InputLocation.Unspecified)]
public Task<ActionResult<AccountResponse>> Reconcile(AccountId id) => ...
Remarks
Most endpoints do not need this. A domain producer names the field that failed but
cannot know where the value came from — the same aggregate method is reachable from a worker,
a message handler, or a test — so it emits Unspecified rather than
asserting a checkable claim that may be false. The response boundary resolves those from the
endpoint's own binding map, which the framework already builds: a violation naming one of the
endpoint's route parameters projects as path, one naming a query parameter it binds
projects as query, and anything left over projects as body when the endpoint
binds a body at all. That is evidence rather than inference, and it is read identically for a
controller action and a minimal API endpoint, so the two hosts cannot drift apart on the wire.
This attribute exists for the cases derivation cannot reach:
- an application that never registered ApiExplorer, where nothing can be derived and Body or Query supplies the residual;
- an endpoint whose derived residual is wrong — it binds a body, but its unlocated violations describe something else;
-
Unspecified, to opt out and keep projecting
unknown.
It only ever supplies the residual — the names the URL does not account for. The URL
still wins: a violation naming a route or query parameter is located from the binding map
whatever this says, because POST /employee/{employeeId} carrying a body can reject both
the id in the URL and a member of the payload, and one declaration cannot describe both.
It never overwrites either. A violation the producer already located — a route, query or header parameter, or a body pointer — passes through untouched.
The nearest declaration wins. Applied to a controller it covers every action, and an action that disagrees overrides it by declaring its own, because MVC appends action metadata after controller metadata and the endpoint's last declaration is the one read. Minimal APIs get the same precedence from convention order, so a route group's declaration is overridden by an endpoint's.
Only Body, Query and Unspecified may be declared. Route and header values are already settled by evidence, and a declaration nobody can justify is a claim nobody checks.
Derivation gets one case wrong, which is the main reason to reach for an override: a body
member sharing a name with a route or query parameter — PUT /employee/{id} carrying
{"id": …} — where the URL is evidence about a different value of the same name.
Confirming that would mean reflecting over the body type's members, which the AOT-friendly
projection path deliberately avoids.
Constructors
InputOriginAttribute(InputLocation)
Initializes a new declaration for the supplied location.
public InputOriginAttribute(InputLocation location)
Parameters
locationInputLocationWhere this endpoint's otherwise-unlocated violations came from. Must be Body, Query, or Unspecified to opt out of an enclosing declaration.
Exceptions
- ArgumentOutOfRangeException
locationis Path, Header, or not a defined InputLocation.
Properties
Location
Gets the location this endpoint declares for violations that arrive without one.
public InputLocation Location { get; }