If you create JSON APIs, you know that sometimes it is useful to return empty response with just status code set. For example when user calls the API to get a document with id 123 and he doesn’t have the rights to this particular document, it is a good practice to return with a status ‘403 Forbidden’. When you do that, you don’t need to add anything to the response – status code is just enough.
My previous project was build with NancyFx, which makes it trivial:
Get["/products/{id}"] = parameters =>; { Product item = repository.Get(parameters.id); if (item == null) { return HttpStatusCode.NotFound; } return item; };
In the ASP.NET 4.6 you had an option to throw a special exception which had a field for response status code:
public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return item; }
Right now I am working on an API built with the new ASP.NET Core and I wanted to do the same. It quickly turned out there is no HttpResponseException class there anymore. There were basically two simple options:
- Change controller method signature to return IActionResult **and return **StatusCode(403) – I didn’t like it as I want to have proper type returned from my controller methods</li>
- Set the Response.StatusCode manually and return null (or 0 if the method returns int) – also didn’t like it as you can’t easily unit test it.</li>
After some googling and searching on StackOverflow, I found a nice solution for this problem – to create a new type of exception: HttpStatusCodeException, containing a field for the status code and a middleware, which will catch these exceptions and reformat the response to just contain the status code.
This is the code for both things:
public class ErrorHandlerMiddleware { public class HttpStatusCodeException : Exception { public HttpStatusCode StatusCode { get; set; } public HttpStatusCodeException(HttpStatusCode statusCode) { StatusCode = statusCode; } } private readonly RequestDelegate _next; public ErrorHandlerMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { try { await _next(context); } catch (HttpStatusCodeException exception) { context.Response.StatusCode = (int) exception.StatusCode; context.Response.Headers.Clear(); } } }
As you can see, the HttpStatusCodeException takes standard HttpStatusCode as a constructor parameter and then it is read and returned by the middleware in the catch statement.
I like this solution because it’s very simple and elegant. I don’t return any content (even null) from the API methods and have proper type checking in the controller methods.