1

I am building an asp.net-core REST api.

How to change the default error: "Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Namespace.Name.Space.Type' because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.

I don't want to expose namespaces or any implementation details with our clients.

This error message is added to 400 - Bad Request response, when the client POST an object different from the required JSON. Could be configurable for the model binder. Any ideas?

2
  • I would catch that exception and return another message to the client. Commented Jan 13, 2020 at 14:05
  • I think something already catches it and wraps it in 400 bad request. Otherwise it would have been 500 internal server error. Commented Jan 13, 2020 at 14:12

1 Answer 1

2

I think something already catches it and wraps it in 400 bad request.

ASP.NET Core 2.1 and later version have added the [ApiController] attribute, which automatically handles model validation errors by returning a BadRequestObjectResult with ModelState passed in.

A simple solution is that you remove the [ApiController] and return your own error message totally:

if (!ModelState.IsValid)
{
    return BadRequest(new { ErrorMessage = "Cannot deserialize" });
}

If you want to keep the ProblemDetails template, you could make use of the InvalidModelStateResponseFactory property.

The default response type for HTTP 400 responses is ValidationProblemDetails class. So, we will create a custom class which inherits ValidationProblemDetails class and define our custom error messages.

public class CustomBadRequest : ValidationProblemDetails
{
    public CustomBadRequest(ActionContext context)
    {
        ConstructErrorMessages(context);
        Type = context.HttpContext.TraceIdentifier;
    }

    private void ConstructErrorMessages(ActionContext context)
    {
        var myerror = "Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Core22APITest.Controllers.TestBindController+RetrieveMultipleResponse' because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.\r\nPath '', line 1, position 1.";
        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]);
                    if(errorMessage == myerror)
                    {
                        Errors.Add(key, new[] { "Cannot deserialize" });
                    }
                    else
                    {
                        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]);
                        if (errorMessages[i] == myerror)
                        {
                            errorMessages[i] =  "Cannot deserialize" ;
                        }
                    }

                    Errors.Add(key, errorMessages);
                }
            }
        }
    }

    string GetErrorMessage(ModelError error)
    {
        return string.IsNullOrEmpty(error.ErrorMessage) ?
            "The input was not valid." :
        error.ErrorMessage;
    }
}

In startup.cs:

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
            .ConfigureApiBehaviorOptions(options =>
            {
                options.InvalidModelStateResponseFactory = context =>
                {
                    var problems = new CustomBadRequest(context);

                    return new BadRequestObjectResult(problems);
                };
            });

Postman Result:

enter image description here

Refer to https://coderethinked.com/customizing-automatic-http-400-error-response-in-asp-net-core-web-apis/

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.