Search code examples
asp.net-coreerror-handlingmodel-bindingjsonconverter

Global error handler including Model Binder errors


I work on a AspNet Core 3.1 web-api project which is being tested by PenTest agency and they flag situations when incorrect input data causes response that contains information about project code internals like below:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-371d845d720a4f4c8ad3d618e4386125-7086a0b4fb30614c-00",
  "errors": {
    "$.flags": [
      "The JSON value could not be converted to System.Int32. Path: $.flags | LineNumber: 0 | BytePositionInLine: 47."
    ]
  }
}

So we decided to alter message for "errors/$.flags" property with something neutral, like "Incorrect input data".

This particular case I sorted out by providing custom JsonConverter< int > attribute class and it works fine being decorated for appropriate model property. Another similar issue where empty collection elements cause such kind of response I fixed by providing custom model binder for complex class on input endpoint parameter (implementation of IModelBinder interface).

The project has a lot of endpoints and input model classes, so providing custom JsonConverters or model binders looks ineffective and not future-proof, as input for every new endpoint will need to be equipped with some kind of solution. So here is the question — is it possible to have some kind of global error handler which will cover model binder errors as well? Standard aspnet core error handler in middleware is not able to work on model binder errors as they happen in action invocation logic before action is actually invoked.

Or probably there is a way to configure System.Text.Json features to provide custom error message for all kinds of JsonExceptions that are thrown in case of binding errors?


Solution

  • For model binding error,you could get 400 Bad Request in asp.net core web api.I suggest that you could custom ValidationProblemDetails to display the error.

    public class CustomBadRequest : ValidationProblemDetails
    {
        public CustomBadRequest(ActionContext context)
        {
            Title = "Invalid arguments to the API";
            Detail = "The inputs supplied to the API are invalid";
            Status = 400;
            ConstructErrorMessages(context);
            Type = context.HttpContext.TraceIdentifier;
            
        }
        private void ConstructErrorMessages(ActionContext context)
        {
            foreach (var keyModelStatePair in context.ModelState)
            {
                var key = keyModelStatePair.Key;
                var errors = keyModelStatePair.Value.Errors;
                if (errors != null && errors.Count > 0)
                {
                    if (errors.Count == 1)
                    {
                        var errorMessage = GetErrorMessage(errors[0]);
                        Errors.Add(key, new[] { errorMessage });
                    }
                    else
                    {
                        var errorMessages = new string[errors.Count];
                        for (var i = 0; i < errors.Count; i++)
                        {
                            errorMessages[i] = GetErrorMessage(errors[i]);
                        }
                        Errors.Add(key, errorMessages);
                    }
                }
            }
        }
        string GetErrorMessage(ModelError error)
        {
            return "Incorrect input data."; 
        }
    }
    

    Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers().ConfigureApiBehaviorOptions(options =>
        {
            options.InvalidModelStateResponseFactory = context =>
            {
                var problems = new CustomBadRequest(context);
                return new BadRequestObjectResult(problems);
            };
        }); 
    }
    

    Result:

    enter image description here