Coder Perfect

In ASP.NET Web API, the best practice is to return failures.

Problem

I’m concerned about how we return errors to clients.

When we obtain an error, do we immediately return an error by throwing HttpResponseException:

public void Post(Customer customer)
{
    if (string.IsNullOrEmpty(customer.Name))
    {
        throw new HttpResponseException("Customer Name cannot be empty", HttpStatusCode.BadRequest) 
    }
    if (customer.Accounts.Count == 0)
    {
         throw new HttpResponseException("Customer does not have any account", HttpStatusCode.BadRequest) 
    }
}

Alternatively, we can collect all errors and deliver them back to the client:

public void Post(Customer customer)
{
    List<string> errors = new List<string>();
    if (string.IsNullOrEmpty(customer.Name))
    {
        errors.Add("Customer Name cannot be empty"); 
    }
    if (customer.Accounts.Count == 0)
    {
         errors.Add("Customer does not have any account"); 
    }
    var responseMessage = new HttpResponseMessage<List<string>>(errors, HttpStatusCode.BadRequest);
    throw new HttpResponseException(responseMessage);
}

This is just a sample code; it doesn’t matter if there are validation problems or server errors; all I want to know is what the best practice is, as well as the advantages and disadvantages of each approach.

Asked by cuongle

Solution #1

I normally throw a HttpResponseException and set the status code based on the exception thrown, and whether the exception is fatal or not determines whether I throw the HttpResponseException right away.

After all, it’s an API that returns responses, not views, therefore I think it’s appropriate to send the consumer a message with the exception and status code. I haven’t had to collect errors and send them back yet because most exceptions are caused by improper parameters or calls, etc.

In my app, for example, if a client requests data and there is none available, I issue a custom NoDataAvailableException and let it bubble to the Web API app, where it is then captured by my custom filter, which sends back an appropriate message along with the right status code.

I’m not sure what the ideal practice is for this, but it’s working for me for now, so I’m sticking with it.

Update:

Several blog entries have been written on the subject since I answered this question:

https://weblogs.asp.net/fredriknormen/asp-net-web-api-exception-handling

(In the nightly builds, this one contains several new features) https://docs.microsoft.com/archive/blogs/youssefm/error-handling-in-asp-net-webapi

Update 2

We have two cases in our error handling process:

Update 3

Following the adoption of Web API 2, we now use the IHttpActionResult interface, specifically the built-in classes for in the System, to return general errors. When they fit, we use the Web.Http.Results namespace, such as NotFound and BadRequest; if they don’t, we expand them, for example, a NotFound result with the following response message:

public class NotFoundWithMessageResult : IHttpActionResult
{
    private string message;

    public NotFoundWithMessageResult(string message)
    {
        this.message = message;
    }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotFound);
        response.Content = new StringContent(message);
        return Task.FromResult(response);
    }
}

Answered by gdp

Solution #2

It was made a lot easier with ASP.NET Web API 2. Consider the following code:

public HttpResponseMessage GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var message = string.Format("Product with id = {0} not found", id);
        HttpError err = new HttpError(message);
        return Request.CreateResponse(HttpStatusCode.NotFound, err);
    }
    else
    {
        return Request.CreateResponse(HttpStatusCode.OK, item);
    }
}

When the item isn’t discovered, the browser is presented with the following content:

HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Date: Thu, 09 Aug 2012 23:27:18 GMT
Content-Length: 51

{
  "Message": "Product with id = 12 not found"
}

Suggestion: Only throw HTTP Error 500 if a catastrophic error has occurred (for example, WCF Fault Exception). Choose an HTTP status code that accurately reflects the state of your data. (For further information, see the apigee link below.)

Links:

Answered by Manish Jain

Solution #3

Because it appears that Validation is causing you more trouble than errors or exceptions, I’ll address both.

Validation

In general, controller actions should use Input Models with validation stated directly on the model.

public class Customer
{ 
    [Require]
    public string Name { get; set; }
}

Then you can use an ActionFilter to send validation messages to the client automatically.

public class ValidationActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var modelState = actionContext.ModelState;

        if (!modelState.IsValid) {
            actionContext.Response = actionContext.Request
                 .CreateErrorResponse(HttpStatusCode.BadRequest, modelState);
        }
    }
} 

Visit http://ben.onfabrik.com/posts/automatic-modelstate-validation-in-aspnet-mvc for more information.

Error handling

It’s ideal to send the client a message that represents the exception that occurred (with relevant status code).

Request is required out of the box. If you want to specify a message, use CreateErrorResponse(HttpStatusCode, message). This, however, connects the function to the Request object, which isn’t necessary.

I normally build my own “safe” exception that I anticipate the client to handle, and then wrap any others in a generic 500 error.

To manage exceptions, you’d use an action filter like this:

public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        var exception = context.Exception as ApiException;
        if (exception != null) {
            context.Response = context.Request.CreateErrorResponse(exception.StatusCode, exception.Message);
        }
    }
}

After that, you can register it on a global scale.

GlobalConfiguration.Configuration.Filters.Add(new ApiExceptionFilterAttribute());

This is a form of exception that I created myself.

using System;
using System.Net;

namespace WebApi
{
    public class ApiException : Exception
    {
        private readonly HttpStatusCode statusCode;

        public ApiException (HttpStatusCode statusCode, string message, Exception ex)
            : base(message, ex)
        {
            this.statusCode = statusCode;
        }

        public ApiException (HttpStatusCode statusCode, string message)
            : base(message)
        {
            this.statusCode = statusCode;
        }

        public ApiException (HttpStatusCode statusCode)
        {
            this.statusCode = statusCode;
        }

        public HttpStatusCode StatusCode
        {
            get { return this.statusCode; }
        }
    }
}

An example of an exception that could be thrown by my API.

public class NotAuthenticatedException : ApiException
{
    public NotAuthenticatedException()
        : base(HttpStatusCode.Forbidden)
    {
    }
}

Answered by Daniel Little

Solution #4

A HttpResponseException can be thrown.

HttpResponseMessage response = 
    this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "your message");
throw new HttpResponseException(response);

Answered by tartakynov

Solution #5

If you’re utilizing ASP.NET Web API 2, the ApiController Short-Method is the most convenient option. A BadRequestResult will be returned as a result of this.

return BadRequest("message");

Answered by Fabian von Ellerts

Post is based on https://stackoverflow.com/questions/10732644/best-practice-to-return-errors-in-asp-net-web-api