Throw Httpresponseexception or Return Request.Createerrorresponse

Throw HttpResponseException or return Request.CreateErrorResponse?

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:

GlobalConfiguration.Configuration.Filters.Add(
new UnhandledExceptionFilterAttribute()
.Register<KeyNotFoundException>(HttpStatusCode.NotFound)

.Register<SecurityException>(HttpStatusCode.Forbidden)

.Register<SqlException>(
(exception, request) =>
{
var sqlException = exception as SqlException;

if (sqlException.Number > 50000)
{
var response = request.CreateResponse(HttpStatusCode.BadRequest);
response.ReasonPhrase = sqlException.Message.Replace(Environment.NewLine, String.Empty);

return response;
}
else
{
return request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
)
);

UnhandledExceptionFilterAttribute class:

using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web.Http.Filters;

namespace Sample
{
/// <summary>
/// Represents the an attribute that provides a filter for unhandled exceptions.
/// </summary>
public class UnhandledExceptionFilterAttribute : ExceptionFilterAttribute
{
#region UnhandledExceptionFilterAttribute()
/// <summary>
/// Initializes a new instance of the <see cref="UnhandledExceptionFilterAttribute"/> class.
/// </summary>
public UnhandledExceptionFilterAttribute() : base()
{

}
#endregion

#region DefaultHandler
/// <summary>
/// Gets a delegate method that returns an <see cref="HttpResponseMessage"/>
/// that describes the supplied exception.
/// </summary>
/// <value>
/// A <see cref="Func{Exception, HttpRequestMessage, HttpResponseMessage}"/> delegate method that returns
/// an <see cref="HttpResponseMessage"/> that describes the supplied exception.
/// </value>
private static Func<Exception, HttpRequestMessage, HttpResponseMessage> DefaultHandler = (exception, request) =>
{
if(exception == null)
{
return null;
}

var response = request.CreateResponse<string>(
HttpStatusCode.InternalServerError, GetContentOf(exception)
);
response.ReasonPhrase = exception.Message.Replace(Environment.NewLine, String.Empty);

return response;
};
#endregion

#region GetContentOf
/// <summary>
/// Gets a delegate method that extracts information from the specified exception.
/// </summary>
/// <value>
/// A <see cref="Func{Exception, String}"/> delegate method that extracts information
/// from the specified exception.
/// </value>
private static Func<Exception, string> GetContentOf = (exception) =>
{
if (exception == null)
{
return String.Empty;
}

var result = new StringBuilder();

result.AppendLine(exception.Message);
result.AppendLine();

Exception innerException = exception.InnerException;
while (innerException != null)
{
result.AppendLine(innerException.Message);
result.AppendLine();
innerException = innerException.InnerException;
}

#if DEBUG
result.AppendLine(exception.StackTrace);
#endif

return result.ToString();
};
#endregion

#region Handlers
/// <summary>
/// Gets the exception handlers registered with this filter.
/// </summary>
/// <value>
/// A <see cref="ConcurrentDictionary{Type, Tuple}"/> collection that contains
/// the exception handlers registered with this filter.
/// </value>
protected ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>> Handlers
{
get
{
return _filterHandlers;
}
}
private readonly ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>> _filterHandlers = new ConcurrentDictionary<Type, Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>>();
#endregion

#region OnException(HttpActionExecutedContext actionExecutedContext)
/// <summary>
/// Raises the exception event.
/// </summary>
/// <param name="actionExecutedContext">The context for the action.</param>
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if(actionExecutedContext == null || actionExecutedContext.Exception == null)
{
return;
}

var type = actionExecutedContext.Exception.GetType();

Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> registration = null;

if (this.Handlers.TryGetValue(type, out registration))
{
var statusCode = registration.Item1;
var handler = registration.Item2;

var response = handler(
actionExecutedContext.Exception.GetBaseException(),
actionExecutedContext.Request
);

// Use registered status code if available
if (statusCode.HasValue)
{
response.StatusCode = statusCode.Value;
}

actionExecutedContext.Response = response;
}
else
{
// If no exception handler registered for the exception type, fallback to default handler
actionExecutedContext.Response = DefaultHandler(
actionExecutedContext.Exception.GetBaseException(), actionExecutedContext.Request
);
}
}
#endregion

#region Register<TException>(HttpStatusCode statusCode)
/// <summary>
/// Registers an exception handler that returns the specified status code for exceptions of type <typeparamref name="TException"/>.
/// </summary>
/// <typeparam name="TException">The type of exception to register a handler for.</typeparam>
/// <param name="statusCode">The HTTP status code to return for exceptions of type <typeparamref name="TException"/>.</param>
/// <returns>
/// This <see cref="UnhandledExceptionFilterAttribute"/> after the exception handler has been added.
/// </returns>
public UnhandledExceptionFilterAttribute Register<TException>(HttpStatusCode statusCode)
where TException : Exception
{

var type = typeof(TException);
var item = new Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>(
statusCode, DefaultHandler
);

if (!this.Handlers.TryAdd(type, item))
{
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> oldItem = null;

if (this.Handlers.TryRemove(type, out oldItem))
{
this.Handlers.TryAdd(type, item);
}
}

return this;
}
#endregion

#region Register<TException>(Func<Exception, HttpRequestMessage, HttpResponseMessage> handler)
/// <summary>
/// Registers the specified exception <paramref name="handler"/> for exceptions of type <typeparamref name="TException"/>.
/// </summary>
/// <typeparam name="TException">The type of exception to register the <paramref name="handler"/> for.</typeparam>
/// <param name="handler">The exception handler responsible for exceptions of type <typeparamref name="TException"/>.</param>
/// <returns>
/// This <see cref="UnhandledExceptionFilterAttribute"/> after the exception <paramref name="handler"/>
/// has been added.
/// </returns>
/// <exception cref="ArgumentNullException">The <paramref name="handler"/> is <see langword="null"/>.</exception>
public UnhandledExceptionFilterAttribute Register<TException>(Func<Exception, HttpRequestMessage, HttpResponseMessage> handler)
where TException : Exception
{
if(handler == null)
{
throw new ArgumentNullException("handler");
}

var type = typeof(TException);
var item = new Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>>(
null, handler
);

if (!this.Handlers.TryAdd(type, item))
{
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> oldItem = null;

if (this.Handlers.TryRemove(type, out oldItem))
{
this.Handlers.TryAdd(type, item);
}
}

return this;
}
#endregion

#region Unregister<TException>()
/// <summary>
/// Unregisters the exception handler for exceptions of type <typeparamref name="TException"/>.
/// </summary>
/// <typeparam name="TException">The type of exception to unregister handlers for.</typeparam>
/// <returns>
/// This <see cref="UnhandledExceptionFilterAttribute"/> after the exception handler
/// for exceptions of type <typeparamref name="TException"/> has been removed.
/// </returns>
public UnhandledExceptionFilterAttribute Unregister<TException>()
where TException : Exception
{
Tuple<HttpStatusCode?, Func<Exception, HttpRequestMessage, HttpResponseMessage>> item = null;

this.Handlers.TryRemove(typeof(TException), out item);

return this;
}
#endregion
}
}

Source code can also be found here.

CreateErrorResponse is losing text

Had to do it like this in the end. This is the only way I could find to get the error message sent back to the client.

    catch (Exception e)
{
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.InternalServerError,
ReasonPhrase = "My error message"
};
}

It is possible to return complex object content in http CreateErrorResponse?

You're not limited to use CreateErrorResponse. It's just there to ease the job done by using already provided HttpError class:

http://msdn.microsoft.com/en-us/library/system.web.http.httperror%28v=vs.108%29.aspx

This class can contain additional set of key/value pairs that can be sent in response body.

However, you can always define your own error class (i.e. ApiError) that will contain exact data that you need, and then return that class by calling, i.e.:

Request.CreateResponse(HttpStatusCode.BadRequest, error);

Where error is instance of your ApiError class.

Unable to throw httpResponseException

Just change your controller implementation to re-throw if it's an HttpResponseException:

try
{
// action implementation
}
catch (Exception e)
{
if (e is HttpResponseException)
{
throw e;
}
// error handling logic
}

But the better answer is that #1 - you should avoid be catching all exceptions, that's bad practice. And #2 - you should use an exception filter instead to do your error handling and not catch exceptions yourself.

Throwing HttpResponseException causes an unhandled exception

WebApi should handle the exception and return the status code contained in it. It is possible that you are running this not in ApiController-derived controller, but one derived from Controller.

You're better off returning HttpResponseMessage with contents of your Employee type. In that case you have better controll over the status code, like this:

var response = Request.CreateResponse(HttpStatusCode.Notfound);
return response

// GET api/employees/12345
public HttpResponseMessage Get(int id)
{
HttpResponseMessage response = null;
var employee = list.FirstOrDefault(e => e.Id == id);

if (employee == null)
{
response = new HttpResponseMessage(HttpStatusCode.NotFound);
}
else
{
response = Request.CreateResponse(HttpStatusCode.OK, employee);
}

return response;
}


Related Topics



Leave a reply



Submit