After reviewing an article Exception Handling in ASP.NET Web API I am a bit confused as to when to throw an exception vs return an error response. I am also left wondering whether it is possible to modify the response when your method returns a domain specific model instead of HttpResponseMessage
...
So, to recap here are my questions followed by some code with case #s:
Questions
Questions regarding Case #1
- Should I always use
HttpResponseMessage
instead of a concrete domain model, so that the message can be customized? - Can the message be customized if you are returning concrete domain model?
Questions regarding Case #2,3,4
- Should I be throwing an exception or returning error response? If the answer is "it depends", can you give situations/examples on when to use one vs the other.
- What is the difference between throwing
HttpResponseException
vsRequest.CreateErrorResponse
? The output to client seems identical... - Should I always use
HttpError
to "wrap" response messages in errors (whether the exception is thrown or error response returned)?
Case Samples
// CASE #1
public Customer Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var notFoundResponse = new HttpResponseMessage(HttpStatusCode.NotFound);
throw new HttpResponseException(notFoundResponse);
}
//var response = Request.CreateResponse(HttpStatusCode.OK, customer);
//response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return customer;
}
// CASE #2
public HttpResponseMessage Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var notFoundResponse = new HttpResponseMessage(HttpStatusCode.NotFound);
throw new HttpResponseException(notFoundResponse);
}
var response = Request.CreateResponse(HttpStatusCode.OK, customer);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return response;
}
// CASE #3
public HttpResponseMessage Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var message = String.Format("customer with id: {0} was not found", id);
var errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
throw new HttpResponseException(errorResponse);
}
var response = Request.CreateResponse(HttpStatusCode.OK, customer);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return response;
}
// CASE #4
public HttpResponseMessage Get(string id)
{
var customer = _customerService.GetById(id);
if (customer == null)
{
var message = String.Format("customer with id: {0} was not found", id);
var httpError = new HttpError(message);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
}
var response = Request.CreateResponse(HttpStatusCode.OK, customer);
response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(300));
return response;
}
Update
To help further demonstrate cases #2,3,4 the following code snippet highlights several options that "can happen" when a customer is not found...
if (customer == null)
{
// which of these 4 options is the best strategy for Web API?
// option 1 (throw)
var notFoundMessage = new HttpResponseMessage(HttpStatusCode.NotFound);
throw new HttpResponseException(notFoundMessage);
// option 2 (throw w/ HttpError)
var message = String.Format("Customer with id: {0} was not found", id);
var httpError = new HttpError(message);
var errorResponse = Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
throw new HttpResponseException(errorResponse);
// option 3 (return)
var message = String.Format("Customer with id: {0} was not found", id);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
// option 4 (return w/ HttpError)
var message = String.Format("Customer with id: {0} was not found", id);
var httpError = new HttpError(message);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, httpError);
}
Another case for when to use
HttpResponseException
instead ofResponse.CreateResponse(HttpStatusCode.NotFound)
, or other error status code, is if you have transactions in action filters and you want the transactions to be rolled back when returning an error response to the client.Using
Response.CreateResponse
will not roll the transaction back, whereas throwing an exception will.If you are not returning HttpResponseMessage and instead are returning entity/model classes directly, an approach which I have found useful is to add the following utility function to my controller
and simply call it with the appropriate status code and message
The approach I have taken is to just throw exceptions from the api controller actions and have an exception filter registered that processes the exception and sets an appropriate response on the action execution context.
The filter exposes a fluent interface that provides a means of registering handlers for specific types of exceptions prior to registering the filter with global configuration.
The use of this filter enables centralized exception handling instead of spreading it across the controller actions. There are however cases where I will catch exceptions within the controller action and return a specific response if it does not make sense to centralize the handling of that particular exception.
Example registration of filter:
UnhandledExceptionFilterAttribute class:
Source code can also be found here.
As far as I can tell, whether you throw an exception, or you return Request.CreateErrorResponse, the result is the same. If you look at the source code for System.Web.Http.dll, you will see as much. Take a look at this general summary, and a very similar solution that I have made: Web Api, HttpError, and the behavior of exceptions
In error situations, I wanted to return a specific error details class, in whatever format the client requested instead of the happy path object.
I want to have my controller methods return the domain specific happy path object and to throw an exception otherwise.
The problem I had was that the HttpResponseException constructors do not allow domain objects.
This is what I eventually came up with
Result
is a class that contains error details, whileProviderCollection
is my happy path result.Case #1
Cases #2-4
These should be equivalent; HttpResponseException encapsulates an HttpResponseMessage, which is what gets returned back as the HTTP response.
e.g., case #2 could be rewritten as
... but if your controller logic is more complicated, throwing an exception might simplify the code flow.
HttpError gives you a consistent format for the response body and can be serialized to JSON/XML/etc, but it's not required. e.g., you may not want to include an entity-body in the response, or you might want some other format.