Home > Software design >  override OnActionExecuting by HttpActionContext in .NET Core
override OnActionExecuting by HttpActionContext in .NET Core

Time:07-22

I have a old MVC program web api with [ValidateModel] attribute:

[Route("login")]
[ValidateModel]
public User Login(LoginModel model)
{

}

Validate the model by code below and return with custom response:

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!actionContext.ModelState.IsValid)
        {
            actionContext.Response = actionContext.Request.CreateResponse(
                    HttpStatusCode.BadRequest,
                    new CustomErrorResult
                    {
                        Succeeded = false,
                        Errors = actionContext.ModelState.Values.SelectMany(
                            o => o.Errors.Select(
                                e => e.ErrorMessage))
                    });
        }

        base.OnActionExecuting(actionContext);
    }
}

Custom error result class

public class CustomErrorResult
{
    public bool Succeeded { get; set; }

    public IEnumerable<string> Errors { get; set; }
}

I am revamping the code to .NET Core now, how do I modify this part to Core version?

I am researched quite some time but still unable solve this, .NET Core seem have a feature can suppress custom response in startup/program file?

CodePudding user response:

When using a controller with the [ApiController] attribute applied, ASP.NET Core automatically handles model validation errors by returning a 400 Bad Request with ModelState as the response body.

Reference: Automatic HTTP 400 responses

One way is that you could suppress this feature by:

services.AddControllers().ConfigureApiBehaviorOptions(options => {
    options.SuppressModelStateInvalidFilter = true;
});

And change your custom ValidateModel:

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(new CustomErrorResult
            {
                Succeeded = false,
                Errors = context.ModelState.Values.SelectMany(
                            o => o.Errors.Select(
                                e => e.ErrorMessage))
            });               
            
        }

        base.OnActionExecuting(context);
    }
}

Another way without customizing ValidateModel is to use a custom response factory in Startup.cs like below:

services.Configure<ApiBehaviorOptions>(o =>
{
    o.InvalidModelStateResponseFactory = actionContext =>
        new BadRequestObjectResult(new BadRequestObjectResult(new CustomErrorResult
        {
            Succeeded = false,
            Errors = actionContext.ModelState.Values.SelectMany(
                    o => o.Errors.Select(
                        e => e.ErrorMessage))
        }));
});
  • Related