Best Practice to Return Errors in ASP.NET Web API

Best practice to return errors in ASP.NET Web API

For me I usually send back an HttpResponseException and set the status code accordingly depending on the exception thrown and if the exception is fatal or not will determine whether I send back the HttpResponseException immediately.

At the end of the day it's an API sending back responses and not views, so I think it's fine to send back a message with the exception and status code to the consumer. I currently haven't needed to accumulate errors and send them back as most exceptions are usually due to incorrect parameters or calls etc.

An example in my app is that sometimes the client will ask for data, but there isn't any data available so I throw a custom NoDataAvailableException and let it bubble to the Web API app, where then in my custom filter which captures it sending back a relevant message along with the correct status code.

I am not 100% sure on what's the best practice for this, but this is working for me currently so that's what I'm doing.

Update:

Since I answered this question a few blog posts have been written on the topic:

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

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

Update 2

Update to our error handling process, we have two cases:

  1. For general errors like not found, or invalid parameters being passed to an action we return a HttpResponseException to stop processing immediately. Additionally for model errors in our actions we will hand the model state dictionary to the Request.CreateErrorResponse extension and wrap it in a HttpResponseException. Adding the model state dictionary results in a list of the model errors sent in the response body.

  2. For errors that occur in higher layers, server errors, we let the exception bubble to the Web API app, here we have a global exception filter which looks at the exception, logs it with ELMAH and tries to make sense of it setting the correct HTTP status code and a relevant friendly error message as the body again in a HttpResponseException. For exceptions that we aren't expecting the client will receive the default 500 internal server error, but a generic message due to security reasons.

Update 3

Recently, after picking up Web API 2, for sending back general errors we now use the IHttpActionResult interface, specifically the built in classes for in the System.Web.Http.Results namespace such as NotFound, BadRequest when they fit, if they don't we extend them, for example a NotFound result with a 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);
}
}

Best practice for error handling with ASP.NET Web API

Error handling in Web API is considered a cross-cutting concern and should be placed somewhere else in the pipeline so the developers doesn’t need to focus on cross-cutting concerns.

You should take a read of Exception Handling in ASP.NET Web API

What happens if a Web API controller throws an uncaught exception? By
default, most exceptions are translated into an HTTP response with
status code 500, Internal Server Error.

and also Global Error Handling in ASP.NET Web API 2

You should try to keep your controller lean as much as possible. Error handling like your original code will only result in duplication of code, and unnecessary concerns for the developers to be aware of. Developers should focus on the core-concern, not the cross-cutting concerns. By just focusing on the core-concern the above code will look like this:

[MyAuthentication]
[MyValidateModel]
public Vb.Order PostOrderItem(Vb.Order order)
{
return Vb.Document.Generate(order);
}

Why so lean?

Because :

if (OAuth.isValid(Request.Headers.GetValues("Token").Single()) != true)
{
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.Unauthorized);
throw new HttpResponseException(httpResponseMessage);
}

can be moved into Authentication Filters in ASP.NET Web API 2
that can be applied locally on the controller/action or globally to return a relevant response.

Model Validation in ASP.NET Web API like this

if (!ModelState.IsValid)
{
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest);
throw new HttpResponseException(httpResponseMessage);
}

Can also be moved into a filter like : .

public class MyValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}

Best practice to handle content in the response after a Web API call results in an exception

EnsureSuccessStatusCode throws an HttpRequestException if the StatusCode is different than 2xx.

In order to gain the most information from the response, you have to retrieve it manually.

The general flow could be described in the following way:

  1. Issue the request inside a try-catch block.
  2. If there was no exception then examine the response's statusCode.
  3. If it is different than the expected one(s) then try to read the response's body

And log everything.

Step #1

HttpResponseMessage response = null;  
try
{
response = await httpClient.PutAsync(...);
}
catch (InvalidOperationException ioEx)
{
//The request message was already sent by the HttpClient instance, but failed due to some protocol violation
HttpClient.CancelPendingRequests();

//TODO: logging
}
catch (TaskCanceledException tcEX)
{
//The request was not completed due to either it's timed out or cancelled
if(!tcEX.CancellationToken.IsCancellationRequested)
HttpClient.CancelPendingRequests();

//TODO: logging
}
catch (HttpRequestException hrEx)
{
//The request failed due to an underlying issue such as network connectivity, DNS failure, server certificate validation.

//TODO: logging
}

Step #2

HttpStatusCodes[] validResponseCodes = new [] {
HttpStatusCode.OK,
HttpStatusCode.Created,
HttpStatusCode.NoContent,
};

if(!validResponseCodes.Contains(response?.StatusCode))
{
//Step #3
}

Step #3

string errorResponse = await response.Content.ReadAsStringAsync();
//Try to parse it if you know the structure of the returned json/xml/whatever

Returning the exception from a web api method

There are a few issues with what you are doing. Let's go over them firstly and then we will go over a better approach.

Issues

  1. Do not catch an exception of type Exception and then tell the client their request is a bad request. If you have a DivideByZeroException, db not found exception, or InvalidOperationException or any other exception, you will tell the client their request is bad. This will clearly not be true.
  2. Your API is asking the client to provide you with a string for a category. So long as they provide it, even if it is "xaoudis garbage", they have done what they are supposed to do: Provide you with a string. Now it is your responsibility to do your best and provide them with a result. The result can be a list of items in that category or an error.

Returning a Response from Web API

Returning a domain object (or a DTO) is fine but if you want to have a finer level of control over the response then use HttpResponseMessage. Here is an examplef (please read the comments in code for more information):

public HttpResponseMessage Get(string category)
{
// Step 1: First check the obvious issues
if (string.IsNullOrWhiteSpace(category))
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}

try
{
// The client has sent us a category. Now we have to do our best to
// satisfy the request.

// Step 2: Optional Step: First check to see if we have the category
string cat = _categoryRepository.Get(category);
if (string.IsNullOrWhiteSpace(cat))
{
var message = new HttpResponseMessage(HttpStatusCode.NotFound);
message.Content = new StringContent($"The category with the name {category} was not found.");
throw new HttpResponseException(message);
}

// Step 3: Category exists so let's return the products
IEnumerable<ArticlesDto> articlesByCategory = _articlesrepository.Find(category);

// Even if the list is empty, we can still return it to tell
// the client 0 items were found
// for the category.
return Request.CreateResponse(HttpStatusCode.OK, articlesByCategory);
}
catch (Exception ex)
{
// Something went wrong on our side (NOT the client's fault). So we need to:
// 1. Log the error so we can troubleshoot it later
// 2. Let the client know it is not their fault but our fault.
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
}

Web API 2

With Web API 2, you can do it like this which is much easier and cleaner. Please change the code as per your requirements.

public IHttpActionResult Get(string category)
{
try
{
// code...

return Ok(articlesByCategory);
}
catch (Exception ex)
{
// Something went wrong on our side (NOT the client's fault). So we need to:
// 1. Log the error so we can troubleshoot it later
// 2. Let the client know it is not their fault but our fault.
return InternalServerError();
}
}

Exception Handling in ASP.NET Web API giving me errors after HttpRequestException

I think that you are looking for is HttpResponseException and not HttpRequestException. If so, you can do it as below:

throw new HttpResponseException(HttpStatusCode.NotFound);

This works, because as you can see in documentation, there is a constructor for that class that takes one parameter of type HttpStatusCode. On the other hand, as you can see here, this is not true for HttpRequestException.

Best Practice : Ways to handle errors and exception in web api controllers?

You should avoid using try/catch in the controller's action.

There are many ways to handle your problem.
The simplest and cleanest solution would probably be to use an ActionFilter to handle the exceptions, something along the lines of:

public class ExceptionAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
Debug.WriteLine(context.Exception);

throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An error occurred!"),
ReasonPhrase = "Deadly Exception"
});
}
}

Then you can just decorate your action with [ExceptionAttribute].
Of course you can extend that to behave differently for different types of exceptions - business exceptions, data exceptions, IO exceptions and so on, and return different status codes and feedback based on that as well.

I recommend you read an excellent article by Fredrik Normen - "ASP.NET Web API Exception Handling" http://weblogs.asp.net/fredriknormen/archive/2012/06/11/asp-net-web-api-exception-handling.aspx.

He provides a great overview of exception handling techniques for Web API.

ASP.NET WebApi return different object on exception

You can use Swagger DataAnnotations and encapusulate the return data to achieve that

First of all create a class to encapsulate the error messages like that

public class Errors
{
public List<string> ErrorMessages { get; set; } = new List<string>();
}

Then use the annotaions like that

For .NET 4.5+ (Fullframework)

[SwaggerResponse(HttpStatusCode.OK, Type = typeof(Student))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(Errors))];
public IHttpActionResult GetStudent(string parametr)
{
try
{
// Database call 1
// Database call 2
return Ok(new Student());
}
catch (Exception ex)
{
Errors errors = new Errors();
errors.ErrorMessages.Add(ex.Message);

return Content(HttpStatusCode.BadRequest, errors);
}
}

For .NET Core

[ProducesResponseType(200, Type = typeof(Student))]
[ProducesResponseType(400, Type = typeof(Errors))]
public IActionResult GetStudent(string parametr)
{
try
{
// Database call 1
// Database call 2
return Ok(new Student());
}
catch (Exception ex)
{
Errors errors = new Errors();
errors.ErrorMessages.Add(ex.Message);

return BadRequest(errors);
}
}

Note that the BadRequest is just an example of return, you should always return the correct Http Status Code message, like 404 to not found, 401 to forbidden and so on



Related Topics



Leave a reply



Submit