I like the ErrorOr library (page and the FluentValidation (page) library. I am trying to connect them.
The validation kicks in, but the response never shows the validation result. Is there a way to wrap them up.
I have a validation interceptor (tried without it)
public class FluentvalidationInterceptor : IValidatorInterceptor
{
public ValidationResult AfterAspNetValidation(ActionContext actionContext, IValidationContext validationContext, ValidationResult result)
{
var validationErrors = new List<ValidationFailure>();
if (!result.IsValid)
{
var errors = result.Errors.GroupBy(x => x.PropertyName).ToList();
foreach (var error in errors)
{
var firstError = error.First();
validationErrors.Add(new ValidationFailure(firstError.PropertyName, firstError.ErrorMessage));
}
}
return new ValidationResult(validationErrors);
}
public IValidationContext BeforeAspNetValidation(ActionContext actionContext, IValidationContext commonContext)
{
return commonContext;
}
}
and register it as following:
public static WebApplicationBuilder ConfigureFluentValidation(this WebApplicationBuilder builder)
{
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
builder.Services.AddFluentValidationAutoValidation(options =>
{
options.ToErrorOr();
options.DisableDataAnnotationsValidation = true;
});
builder.Services.AddFluentValidationClientsideAdapters();
builder.Services.AddValidatorsFromAssemblyContaining<LoginRequestValidator>();
return builder;
}
The ErrorOr is configured like this:
public static class ExceptionHandlingConfiguration
{
public static IServiceCollection AddGlobalErrorHandling(this IServiceCollection services)
{
services.AddProblemDetails(options =>
{
options.CustomizeProblemDetails = problemDetailsContext =>
{
var errors = problemDetailsContext.HttpContext?.Items[HttpContextItemKeys.Errors] as List<Error>;
problemDetailsContext.ProblemDetails.Extensions["traceId"] = problemDetailsContext?.HttpContext?.TraceIdentifier;
if (errors is not null)
{
problemDetailsContext?.ProblemDetails.Extensions.Add("errorCodes", errors);
}
};
});
return services;
}
public static WebApplication UseGlobalErrorHandling(this WebApplication app)
{
app.UseExceptionHandler("/error");
app.Map("/error", (HttpContext httpContext) =>
{
return Results.Problem();
});
return app;
}
}
I have a global handling endpoint also:
[ApiController]
[Route("[controller]")]
[ProducesResponseType(typeof(ProblemDetails), (int)HttpStatusCode.InternalServerError)]
public class BaseController :ControllerBase
{
protected IActionResult Problem(ICollection<Error> errors)
{
HttpContext.Items[HttpContextItemKeys.Errors] = errors;
if (errors.All(e => e.Type == ErrorType.Validation))
{
var modelStateDictionary = new ModelStateDictionary();
foreach (var error in errors)
{
modelStateDictionary.AddModelError(error.Code, error.Description);
}
return ValidationProblem(modelStateDictionary);
}
if (errors.Any(e => e.Type == ErrorType.Unexpected))
{
return Problem();
}
var firstError = errors.First();
var statusCode = firstError.Type switch
{
ErrorType.NotFound => StatusCodes.Status404NotFound,
ErrorType.Validation => StatusCodes.Status400BadRequest,
ErrorType.Conflict => StatusCodes.Status409Conflict,
_ => StatusCodes.Status500InternalServerError
};
return Problem(statusCode: statusCode, title: firstError.Description);
}
}
I am interested to learn if there is any way to make these two libraries work together?