In my web API using the MVC 4 Web API framework, if there is an exception I'm throwing a new HttpResponseException
:
if (!Int32.TryParse(id, out userId))
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid id"));
This returns an object to the client: {"message":"Invalid id"}
. I want to return a more detailed object:
{
"status":-1,
"substatus":3,
"message":"Could not find user"
}
How would I go about this? Is the best way to serialize my error object and set it in the response message? I've looked into ModelStateDictionary
and came up with this, but it's not a clean output:
var msd = new ModelStateDictionary();
msd.AddModelError("status", "-1");
msd.AddModelError("substatus", "3");
msd.AddModelError("message", "invalid stuff");
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, msd));
I think this will do the trick:
Create a custom exception class for the business layer:
public class MyException: Exception
{
public ResponseStatus Status { get; private set; }
public ResponseSubStatus SubStatus { get; private set; }
public new string Message { get; private set; }
public MyException()
{}
public MyException(ResponseStatus status, ResponseSubStatus subStatus, string message)
{
Status = status;
SubStatus = subStatus;
Message = message;
}
}
Create a static method to generate a HttpError
from an instance of MyException
. I'm using reflection here so I can add properties to MyException
and always have them returned w/o updating Create
:
public static HttpError Create<T>(MyException exception) where T:Exception
{
var properties = exception.GetType().GetProperties(BindingFlags.Instance
| BindingFlags.Public
| BindingFlags.DeclaredOnly);
var error = new HttpError();
foreach (var propertyInfo in properties)
{
error.Add(propertyInfo.Name, propertyInfo.GetValue(exception, null));
}
return error;
}
I currently have a custom attribute for a general exception handler. All exceptions of type MyException
will be handled here:
public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
var statusCode = HttpStatusCode.InternalServerError;
if (context.Exception is MyException)
{
statusCode = HttpStatusCode.BadRequest;
throw new HttpResponseException(context.Request.CreateErrorResponse(statusCode, HttpErrorHelper.Create(context.Exception)));
}
if (context.Exception is AuthenticationException)
statusCode = HttpStatusCode.Forbidden;
throw new HttpResponseException(context.Request.CreateErrorResponse(statusCode, context.Exception.Message));
}
}
I'll play around with this a bit more and update as I find holes in this plan.