Miguel Moura
Miguel Moura

Reputation: 39384

Custom error response in Web Api

On an Web API controller I have the following:

if (!ModelState.IsValid)
  return BadRequest();

BadRequest: https://msdn.microsoft.com/en-us/library/system.web.http.apicontroller_methods%28v=vs.118%29.aspx

I would like to create a custom error message to pass errors as json.

Something like:

if (!ModelState.IsValid)
  return ModelHasErrors(errors);

How can I create a custom error response?

Upvotes: 0

Views: 5902

Answers (3)

Miroslav Holec
Miroslav Holec

Reputation: 3217

The simplest way is

return Request.CreateErrorResponse(HttpStatusCode.NotFound, ModelState);

I am used to use special object, that represents error response. I can add my specific (translated) message, status code, etc.

public class ErrorModel
{
    public ErrorModel(HttpStatusCode statusCode, string message)
    {
        StatusCode = (int)statusCode;
        Message = message;
        ValidationErrors = new Dictionary<string, ModelErrorCollection>();
    }

    public ErrorModel(HttpStatusCode statusCode)
    {
        StatusCode = (int)statusCode;
        ValidationErrors = new Dictionary<string, ModelErrorCollection>();
    }

    public string Message { get; set; }
    public int StatusCode { get; set; }
    public Dictionary<string, ModelErrorCollection> ValidationErrors { get; set; }
    public Exception Exception { get; set; }
}

Then I have extension for CreateCustomResponse

public static class ApiExtensions
    {
        public static HttpResponseMessage CreateCustomResponse(this HttpRequestMessage request, HttpStatusCode statusCode, string errorMessage)
        {
            var errorMessageModel = new ErrorModel(statusCode, errorMessage);

            return request.CreateResponse(statusCode, errorMessageModel);
        }

        public static HttpResponseMessage CreateCustomResponse(this HttpRequestMessage request, HttpStatusCode statusCode, Exception exception, string errorMessage = "")
        {
            if (string.IsNullOrEmpty(errorMessage) && exception != null)
            {
                errorMessage = exception.Message;
            }

            var errorMessageModel = new ErrorModel(statusCode, errorMessage)
            {
                Exception = exception
            };

            return request.CreateResponse(statusCode, errorMessageModel);
        }

        public static HttpResponseMessage CreateCustomResponse(this HttpRequestMessage request,
            HttpStatusCode statusCode, ModelStateDictionary modelState, string errorMessage = "")
        {
            if (string.IsNullOrEmpty(errorMessage))
            {
                errorMessage = ApiValidationMessages.GeneralModelIsNotValid;
            }

            var errorMessageModel = new ErrorModel(statusCode, errorMessage);

            foreach (var error in modelState.Where(x => x.Value.Errors != null && x.Value.Errors.Any()))
            {
                errorMessageModel.ValidationErrors.Add(error.Key.Replace("model.", ""), error.Value.Errors);
            }

            return request.CreateResponse(statusCode, errorMessageModel);
        }
    }

And finally in my controllers I just call:

return Request.CreateCustomResponse(HttpStatusCode.NotFound, ApiHttpResultMessages.NotFound);

You can find inspiration in my CodePlex project Web API Design: https://webapidesign.codeplex.com/

Upvotes: 3

elolos
elolos

Reputation: 4450

You could return a HttpResponseMessage using an object containing your error messages (in this example errors) as content:

return new HttpResponseMessage(HttpStatusCode.BadRequest)
        {
            Content = new ObjectContent(typeof(ErrorClass), errors, new JsonMediaTypeFormatter())
        };

More information about how to return action results can be found here.

You can obviously reuse the above code by creating a method and call it like in your example: ModelHasErrors(errors).

However, if you often find yourself returning the same response, a nice option would be to create an custom exception filter which would return the same response whenever ModelState.IsValid is false.

Upvotes: 0

JotaBe
JotaBe

Reputation: 39025

You can return directly whichever object you want, and it will be serialized as JSON. It can even be an anonymous class object created with new { }

On The client side you have to check if you've received a regurlar response, or the error object, which can be easyly donde by checking the existence of some property of your custom error object.

Upvotes: 0

Related Questions