Operate An Exception Zoo
We’re coming towards the end of our series on Exception Management. We’ve covered many areas, from why we should throw custom exceptions to why it’s a good idea for us to derive specific exceptions from generalised base exceptions.
Last time we explored how we can catch exceptions derived from an abstract InputDataException
in a single catch clause:
catch (InputDataException ex) { // Handle all exceptions derived from InputDataException }
This approach works well as long as we can manage exceptions derived from InputDataException
in the same way. Since the root cause of all specific InputDataException
s is invalid input data, we should aim to handle those consistently—say, a return a 400 - Bad Request
when in a web context. Something like this:
catch (InputDataException ex) { return HandleAs400BadRequest(ex); }
Since we spent much of our time programming for the web, let’s stay with the web context for now.
In a web server application, we respond to requests with an HTTP status code. When our server application code runs into a problem, the HTTP response status code can be one of two types:
- It’s the client‘s fault – The client has sent through bad data. Error codes: 4xx (eg. 400 – Bad Request)
- It’s the server‘s fault – The server has failed to respond to a valid request. Error codes: 5xx (eg. 500 – Internal Server Error)
Let’s look more closely at the 4xx client errors:
- 400 – Bad Request. The server should respond with this HTTP status code when the client is sending through invalid data, and a business rule in our server-side code is triggered.
- 401 – Unauthorised. The authentication details in the request are either lacking, incomplete or wrong. This status code has the wrong name – it should be 401 – Unauthenticated.
- 403 – Forbidden. The system is letting the client know that they are not authorised to perform this action.
- 404 – Not Found. The server could not find the given resource. It may not ever have existed, or someone may have deleted it already.
_________________________
Authentication vs Authorisation – A Quick Interlude
I have noticed that there exists confusion about the difference between the concepts of Authentication and Authorisation. Let’s clear this up.
Authentication means the identification of the acting party, a person, service or account.
On the other hand, Authorisation is the assigning of rights (or lack thereof) to carry out a particular action on a system.
A few more pointers:
- Authentication must always come before Authorisation.
- Authorisation is always binary, either true or false.
- Authorisation applies to an action or resource.
Real-world example: Exclusive Nightclub
Let’s consider these terms in the situation where you’re trying to get into an exclusive nightclub—when the bouncer is asking you for your Id, that is a request for Authentication. Say you haven’t got your Id on you. The bouncer will decline your access request outright (i.e. Authorisation) since you cannot identify yourself. If you have your Id, you can at least pass the Authentication step—you can identify yourself. Now the bouncer checks whether you are on ‘The List’, i.e. that you’re Authorised and allowed to enter (or not).
_________________________
OK, let’s come back to handling exceptions.
To recap, HTTP 400 - Bad Request
, 401 - Unauthorised
, 403 - Forbidden
and 404 - Not Found
are the most typical client-caused HTTP error codes on the web.
Would it not be useful if in our server web code we could catch precise exceptions that in turn convert to error responses with these error codes?
For example, these exceptions could convert to these HTTP status codes:
- InputDataException ⇒
400 - Bad Request
- UnauthenticatedException ⇒
401 - Unauthorised
- UnauthorizedException ⇒
403 - Forbidden
- NotFoundException ⇒
404 - Not Found
- and so on…
This mapping could be pretty helpful, right?!
So, we could catch specific exceptions by base type, the left-hand side of the mapping, and manage them as per the right-hand side, e.g. emit a response of the matching HTTP status code. The code might look like this:
catch (InputDataException ex) { return HandleAs400BadRequest(ex); } catch (UnauthenticatedException ex) { return HandleAs401Unauthorised(ex); } catch (UnauthorisedException ex) { return HandleAs403Forbidden(ex); } catch (NotFoundException ex) { return HandleAs404NotFound(ex); } catch (ServerException ex) { return HandleAs502BadGateWay(ex); } // ... and so on.
Even though this strategy has compressed the total number of catch
blocks considerably, the list is still long. Next time I show a way of designing a compact exception handler.
So what we have here is a zoo of exceptions. In zoos, animals are grouped by their Class: Reptiles, Mammals, Fish, etc.
We can do the same with our exception types—group as InputDataException
, UnauthorisedException
, ServerException
, etc.
In conclusion, consider grouping your specific exceptions by super-type and systematically handling only the super-types.
Leave a Reply
Want to join the discussion?Feel free to contribute!