FluentValidation.AutoValidation icon indicating copy to clipboard operation
FluentValidation.AutoValidation copied to clipboard

Enhance IFluentValidationAutoValidationResultFactory and support async operations

Open baranacikgoz opened this issue 10 months ago • 2 comments

The current implementation of the IFluentValidationAutoValidationResultFactory relies on returning a IResult object directly.

However, this approach is limiting and won't allow us to use built-in IProblemDetailsService. As an example, I have an IProblemDetailsService setup in my project, and whenever I'll return a problem details, I rely on it, it adds the instance, traceId, environment, node and etc. automatically for me;

image

The current implementation of IFluentValidationAutoValidationResultFactory is forcing us to return an IResult object. Which means, one must duplicate the problem details setup once in services.AddProblemDetails and again in IFluentValidationAutoValidationResultFactory.

My proposal is to change the method signature from

IResult CreateResult(EndpointFilterInvocationContext context, ValidationResult validationResult);

To

ValueTask WriteResult(EndpointFilterInvocationContext context, ValidationResult validationResult);

Hence, allow us to use IProblemDetailsService.WriteAsync like this;

internal sealed class CustomFluentValidationResultFactory(
    IStringLocalizer<ResxLocalizer> localizer,
    IProblemDetailsService problemDetailsService
    ) : IFluentValidationAutoValidationResultFactory
{

    public async ValueTask WriteResult(EndpointFilterInvocationContext context, ValidationResult validationResult)
    {
        var problemDetails = new ProblemDetails
        {
            Status = (int)HttpStatusCode.BadRequest,
            Title = localizer[nameof(HttpStatusCode.BadRequest)],
        };

        problemDetails.AddErrorKey(nameof(ValidationFailure));
        problemDetails.AddErrors(validationResult.Errors.Select(x => x.ErrorMessage));

        context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
        await problemDetailsService.WriteAsync(new ProblemDetailsContext
        {
            HttpContext = context.HttpContext,
            ProblemDetails = problemDetails,
        });
    }
}

I'd love to help this refactor. What are your thoughts?

baranacikgoz avatar Jan 15 '25 21:01 baranacikgoz

Hi @baranacikgoz, interesting thoughts. Let me think on this one for a little bit. If we decide to do it now is a great time since I will start work on the v2 branch soon which allows me to add breakign changes to the code base.

More improvements: https://github.com/SharpGrip/FluentValidation.AutoValidation/issues/12

mvdgun avatar Jan 19 '25 02:01 mvdgun

@baranacikgoz I am currently working on a factory rewrite. So far this is what I have:

public Task<IActionResult> Create(MvcAutoValidationContext mvcAutoValidationContext);

The new MvcAutoValidationContext contains the ActionExecutingContext, ValidationProblemDetails and the validation failures. You are still required to return a IActionResult (but the method is now async). Does this help your case?

mvdgun avatar Sep 11 '25 17:09 mvdgun