Skip to main content
Version: 0.3.x (0.3.0)

Class ValueObjectEndpointConventionBuilderExtensions

Namespace: AdCodicem.ValueObjects.AspNetCore.Http
Assembly: AdCodicem.ValueObjects.AspNetCore.Http.dll

Answers a value object a minimal API endpoint could not bind with problem details carrying the code of the rule it broke.

public static class ValueObjectEndpointConventionBuilderExtensions

Inheritance​

object ← ValueObjectEndpointConventionBuilderExtensions

Inherited Members​

object.Equals(object?), object.Equals(object?, object?), object.GetHashCode(), object.GetType(), object.MemberwiseClone(), object.ReferenceEquals(object?, object?), object.ToString()

Methods​

WithValueObjectProblemDetails<TBuilder>(TBuilder)​

Answers a value object these endpoints could not bind with problem details carrying the code of the rule it broke.

public static TBuilder WithValueObjectProblemDetails<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder

Parameters​

builder TBuilder

The endpoints to cover.

Returns​

TBuilder

The same builder, so calls can be chained.

Type Parameters​

TBuilder

The builder: a route group, one endpoint, or any other convention builder.

Remarks​

A minimal API refuses a value object it cannot bind with a 400 that says nothing of why. On the endpoints this covers, the refusal is answered with the validation problem MVC writes, errors holding the message of each rule a value broke, and mapping each member to the stable code of that rule, read from the same TryParse the binder ran. A route value, a query value and a header are covered, under the name they bind from, which [FromRoute], [FromQuery] and [FromHeader] may set; so are the members of an [AsParameters] type and an array of value objects read from the query string, whose refused elements are listed under its name with the code of the first. A required value object that is absent is reported with .

A filter is added to each endpoint whose handler may bind a value object from text. When it runs, it reads one thing, the status the binder left: anything but a 400 goes on untouched, and only a refusal pays for the second parse. A 400 that no value object caused, a refused or parameter for instance, keeps the framework's answer: a parameter that is not a value object has no rule code to report, and is not listed beside one that is. The convention acts once on an endpoint it covers twice, through a route group and the endpoint itself.

Empty text follows the binder that ran. The reflection-based binding parses it, so that ?country= for a CountryCode? is reported with the rule that refused it. The Request Delegate Generator refuses no empty query text: it binds null to a nullable value object, and the default instance, which no rule has checked, to one that cannot be null, an array element included, so that nothing is reported there. It takes an empty header for an absent one, reported with when the parameter is required.

The binder refuses a request body before any filter runs, and the framework answers it itself. With RouteHandlerOptions.ThrowOnBadRequest on, the default in Development, the binder throws instead, before the filter runs, and the exception handler registered by answers a body as well as a route, query or header value with the same problem details. A form field and an array read from headers are not covered. Under AddValidation(), whose filter runs before every other, a request that also breaks a DataAnnotations rule is answered with that rule alone.

The filter closes the check of each value object over its type through the descriptor's visitor, so that it runs in a native binary. Under native AOT, a value object nothing registered, one written by hand or a construction of a generic one, cannot be described there, and its refusal is left unexplained: register it as the JSON package asks, ValueObjectRegistry.Register<TSelf, TValue>(TSelf.Schema) for one written by hand, with its generated converter for a construction.

The response is written with the HTTP JSON options, which, without reflection-based serialization, as in a native binary, know a type only through a serializer context: register AddProblemDetails(), which chains the framework's own for problem details into them. Without it, or a context of the application's listing HttpValidationProblemDetails and JsonElement, the covered endpoints fail to build, when the first request needs them, with an naming the call, rather than answer each refusal with a 500 where the framework answers a 400.

The messages are the English ones of the rules. answers in the application's language.

Exceptions​

ArgumentNullException

builder is null.

WithValueObjectProblemDetails<TBuilder>(TBuilder, Action<ValueObjectMessageOptions>)​

Answers a value object these endpoints could not bind with problem details carrying the code of the rule it broke, and the message the application's resource gives that code.

public static TBuilder WithValueObjectProblemDetails<TBuilder>(this TBuilder builder, Action<ValueObjectMessageOptions> configure) where TBuilder : IEndpointConventionBuilder

Parameters​

builder TBuilder

The endpoints to cover.

configure Action<ValueObjectMessageOptions>

Names the resource the messages are read from, through .

Returns​

TBuilder

The same builder, so calls can be chained.

Type Parameters​

TBuilder

The builder: a route group, one endpoint, or any other convention builder.

Remarks​

The endpoints are covered as covers them, and errors holds, for each refused member, the message the resource holds under the key of the rule's code, ValueObject.value_object.out_of_range, looked up after ValueObject.value_object.out_of_range.minimum or .maximum for a type declaring one bound alone. It is formatted with the name the member binds from as {0}, the name of the value object for a request body, whose JSON path may hold a refused dictionary key, and the arguments the type's rules supply from {1}: a length, both bounds, the known values. The codes under are the same in every language. A key the resource lacks keeps the English message.

The resource is read through the request's IStringLocalizerFactory: register builder.Services.AddLocalization(), without which a refusal throws an naming it, and app.UseRequestLocalization(), so that the request chooses the culture. The current UI culture chooses the template and the current culture formats the arguments. The exception handler that answers with ThrowOnBadRequest on runs in the request's culture, whatever the order of UseRequestLocalization() and UseExceptionHandler(). A template quoting an argument its code does not supply throws its , which is not caught.

The options belong to the endpoint: covered twice, through a route group and the endpoint itself, it takes those of the convention that runs last, the endpoint's own over its group's, and a call without options keeps the other's.

Exceptions​

ArgumentNullException

builder or configure is null.