arrow-left arrow-right brightness-2 chevron-left chevron-right circle-half-full dots-horizontal facebook-box facebook loader magnify menu-down RSS star Twitter twitter GitHub white-balance-sunny window-close
Consistent error handling with Nancy
4 min read

Consistent error handling with Nancy

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

In moving the Octopus portal to Nancy, I wanted to have some consistency in the way errors are handled. In ASP.NET MVC/WebAPI I had a number of different filters that would handle different error conditions and return them to the user. I also then had a catch-all in Global.asax that tried to render a friendly error page if something really bad happened.

There are two dimensions to the way errors are handled. First, there are different kinds of errors. Some are the user's fault (not authenticated, no permissions to perform an action, 404, validation failure). Others are our fault (internal server error). I've documented the main error status codes Octopus uses in the API documentation.

Secondly, there are different user experiences that will depend on the error type and request.

  • If the client prefers a JSON response, we'll send the status code and a JSON result containing the error details. For 500 exceptions we'll include the exception details; for other errors we'll include a description of what caused the problem and potential solutions.
  • If the client prefers a HTML response, we might redirect to a log in screen for 401 errors. Other errors will show a friendly error page describing the problem.

For example, if the user is using a web browser (Accept: text/html) and they navigate to a page that doesn't exist, they'll get:

A 404 page

While a user of the JSON API (Accept: application/json) will receive:

A 404 JSON response

And the same will happen if the server encounters an exception while processing the request:

Server exception

While API clients see:

Server exception from JSON

400: Bad request and 403: Forbidden errors will be handled in a similar way: JSON responses for API clients, and HTML responses for real users.

401 errors will be handled slightly differently. From the API, we'll return a JSON error indicating that the API key is probably invalid, while HTML clients will receive a 302 redirect to the log in page.

Implementation

I've only been playing with Nancy for a couple of days, so the implementation took some experimentation and could probably be much simpler. First, Nancy has very good documentation (I would say more usable than ASP.NET WebAPI), so this page in the Nancy docs was good reading on the subject:

My strategy starts with a custom JsonResponse called an ErrorResponse (view the full gist). This can be created from an exception or a single message. For example:

Get["/bad"] = c => { return ErrorResponse.FromMessage("Something bad happened"); };
Get["/buggy"] = c => { return ErrorResponse.FromException(new DivideByZeroException()); };

I then customized my Nancy bootstrapper to log and translate any unhandled exceptions into an ErrorResponse:

protected override void RequestStartup(ILifetimeScope requestContainer, IPipelines pipelines, NancyContext context)
{
    pipelines.OnError.AddItemToEndOfPipeline((z, a) =>
    {
        log.Error("Unhandled error on request: " + context.Request.Url + " : " + a.Message, a);
        return ErrorResponse.FromException(a);
    });

    base.RequestStartup(requestContainer, pipelines, context);
}

Now that I am capturing error details and turning them into meaningful responses, I need a solution to render the responses in an appropriate way. For this, I implemented a custom IStatusCodeHandler. My status code handler (full gist) decides whether a HTML response would be more appropriate based on the accept headers, and if so, turns the ErrorResponse into a new response type to render a HTML error page:

The final piece is my custom Response that renders the error HTML page from an embedded resource. I put this together based on some existing code in Octopus, so I'm not currently using a real Nancy view engine to do it. I'll be experimenting with turning this view into a Razor view soon.

The final piece of the puzzle is to ensure IIS doesn't attempt to render its own custom error pages instead of mine. To do this, it's as easy as a web.config entry:

<system.webServer>
  <httpErrors errorMode="Custom" existingResponse="PassThrough" />
  </.../>

To recap, my error handling strategy includes:

  • A custom response type that indicates a meaningful error with details
  • A status code handler that translates any other responses and determines whether to render them in HTML
  • Another custom response type that renders the friendly error page (may not be required)

Can it be improved? I'm sure it can, since as I said, I've only been playing with Nancy for a few days. Leave a comment in the box below!

Paul Stovell's Blog

Hello, I'm Paul Stovell

I'm a Brisbane-based software developer, and founder of Octopus Deploy, a DevOps automation software company. This is my personal blog where I write about my journey with Octopus and software development.

I write new blog posts about once a month. Subscribe and I'll send you an email when I publish something new.

Subscribe

Comments