Search code examples
c#asp.net-corefluentvalidationaction-filter

FluentValidation use custom IActionFilter


I have a custom IActionFilter which I register with my application like so:

services.AddControllers(options => options.Filters.Add(new HttpResponseExceptionFilter()));

The class looks like this:

public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
{
    public int Order { get; set; } = int.MaxValue - 10;

    public void OnActionExecuting(ActionExecutingContext context)
    {
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.Exception == null) return;

        var attempt = Attempt<string>.Fail(context.Exception);

        if (context.Exception is AttemptException exception)
        {
            context.Result = new ObjectResult(attempt)
            {
                StatusCode = exception.StatusCode,
            };
        }
        else
        {
            context.Result = new ObjectResult(attempt)
            {
                StatusCode = (int)HttpStatusCode.InternalServerError,
            };
        }

        context.ExceptionHandled = true;
    }
}

I would expect that when validating it would invoke the OnActionExecuting method. So I added this code:

public void OnActionExecuting(ActionExecutingContext context)
{
    if (!context.ModelState.IsValid)
    {
        context.Result = new BadRequestObjectResult(context.ModelState);
    }
}

And I put a breakpoint at the start of the method, but when I run my application and try to post an invalid model, I get this response:

{
    "errors": {
        "Url": [
            "'Url' is invalid. It should start with 'https://www.youtube.com/embed'",
            "'Url' is invalid. It should have the correct parameter '?start='"
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|87e96062-42181357ba1ef8c5."
}

How can I force FluentValidation to use my filter?


Solution

  • The best solution I found was:

    .ConfigureApiBehaviorOptions(options =>
    {
        options.InvalidModelStateResponseFactory = context =>
        {
            var messages = context.ModelState.Values
                .Where(x => x.ValidationState == ModelValidationState.Invalid)
                .SelectMany(x => x.Errors)
                .Select(x => x.ErrorMessage)
                .ToList();
    
            return new BadRequestObjectResult(
                Attempt<string>.Fail(
                    new AttemptException(string.Join($"{Environment.NewLine}", messages))));
        };
    })