Class ValidationErrorsContext
Provides a context for collecting validation errors during JSON deserialization. Uses AsyncLocal to maintain thread-safe, request-scoped error collection.
public static class ValidationErrorsContext
- Inheritance
-
ValidationErrorsContext
- Inherited Members
Examples
using (ValidationErrorsContext.BeginScope())
{
// Deserialize JSON - errors are collected
var dto = JsonSerializer.Deserialize<CreateUserDto>(json, options);
// Check for collected errors
var error = ValidationErrorsContext.GetUnprocessableContent();
if (error is not null)
{
return Results.Problem(detail: error.Detail, statusCode: 422);
}
}
Remarks
This class enables the pattern of collecting all validation errors from value objects during JSON deserialization, rather than failing on the first error. This allows returning a comprehensive list of validation failures to the client.
The context is automatically scoped per async operation, making it safe for use in concurrent web request scenarios.
Properties
CurrentPropertyName
Gets or sets the current property name being deserialized. Used by ValidatingJsonConverter (reflection mode) and AOT-generated converters to determine the field name for validation errors.
public static string? CurrentPropertyName { get; set; }
Property Value
Remarks
In reflection mode, the property name is set by PropertyNameAwareConverter<T>
during JSON deserialization. In AOT mode, generated converters read this property and fall back
to a camel-cased type name when no scope is active. Using AsyncLocal ensures thread-safety
and proper isolation across concurrent requests.
HasErrors
Gets whether any validation errors have been collected in the current scope.
public static bool HasErrors { get; }
Property Value
Methods
AddBodyError(string, string)
Adds a validation error for a specific field of the request body to the current scope.
public static void AddBodyError(string fieldName, string errorMessage)
Parameters
fieldNamestringThe name of the field that failed validation.
errorMessagestringThe validation error message.
Remarks
If no scope is active, this method is a no-op. Unlike AddError(string, string), this asserts that the value came from the request body, so the resulting violation is located at Body. As with that overload, the violation carries the neutral sentinel code because no code is supplied.
AddBodyError(string, string, string, IReadOnlyDictionary<string, ValidationArgValue>?)
Adds a coded validation error for a specific field of the request body to the current scope.
public static void AddBodyError(string fieldName, string code, string message, IReadOnlyDictionary<string, ValidationArgValue>? args = null)
Parameters
fieldNamestringThe name of the field that failed validation.
codestringThe machine-readable reason code.
messagestringThe human-readable validation error message.
argsIReadOnlyDictionary<string, ValidationArgValue>Optional arguments that parameterize the message.
Remarks
If no scope is active, this method is a no-op. The resulting violation is located at Body.
AddBodyError(InvalidInput)
Adds all field violations and rule violations from an existing Error.InvalidInput to the current scope, asserting that they describe the request body.
public static void AddBodyError(Error.InvalidInput unprocessableContent)
Parameters
unprocessableContentError.InvalidInputThe error whose violations should be merged.
Remarks
If no scope is active, this method is a no-op.
Violations are merged under the body-context rebase rule: a violation with no explicit location is prefixed with the current ancestor path and promoted to Body; one already located in the body is prefixed; and one carrying an explicit Query, Path or Header location passes through untouched, because a body ancestor is meaningless for a named parameter.
AddError(string, string)
Adds a validation error for a specific field to the current scope.
public static void AddError(string fieldName, string errorMessage)
Parameters
fieldNamestringThe name of the field that failed validation.
errorMessagestringThe validation error message.
Remarks
If no scope is active, this method is a no-op. Called by the framework's reflection-mode converters and by AOT-generated scalar value converters to surface deserialization failures as 422 responses via ScalarValueValidationMiddleware.
The violation carries the neutral sentinel code, because this overload accepts a message and no code — there is nothing truthful to report. To emit a real reason code, build the violation yourself and use AddError(InvalidInput), which preserves the code, args, and detail unchanged.
AddError(InvalidInput)
Adds all field violations and rule violations from an existing Error.InvalidInput to the current scope.
public static void AddError(Error.InvalidInput unprocessableContent)
Parameters
unprocessableContentError.InvalidInputThe error whose violations should be merged.
Remarks
If no scope is active, this method is a no-op.
The merge preserves each field violation's full structure — including its ReasonCode, Args, and Detail — and also preserves any top-level Rules entries. Used by the framework's reflection-mode converters and by AOT-generated scalar value converters to surface rich, structured validation failures.
BeginScope()
Begins a new validation error collection scope.
public static IDisposable BeginScope()
Returns
- IDisposable
An IDisposable that ends the scope when disposed.
Remarks
Always use this in a using statement or block to ensure proper cleanup. Nested scopes are supported; each scope maintains its own error collection and starts at the document root, so the ambient current-property name and ancestor path are reset for the new scope and restored when it is disposed.
GetUnprocessableContent()
Gets the aggregated Error.InvalidInput from the current scope, or null if no errors were collected.
public static Error.InvalidInput? GetUnprocessableContent()
Returns
- Error.InvalidInput
An Error.InvalidInput containing all collected field and rule violations, or
nullif no validation errors were recorded.