Developing software in the Real World

Setting HTTP status code based on Exception in Slim 4

One thing that’s quite convenient is to be able to throw an exception with a valid HTTP code set and have that code sent to the client.

For example, you may have:

With the standard Slim 4 error handler, this response is sent to the client:

Ideally we want the status code to be 404.

Option 1: Use an HttpException

The simplest solution is to use one of Slim’s HttpException classes:

This is only useful in a Request Handler as you need a Request object, but the expected response is sent to the client:

Simple and easy!

Option 2: Override the ErrorMiddleware

There are situation when you can’t simply replace the exception thrown. For example, you’re updating an application from Slim 3 and you have hundreds of customised exceptions already throwing, or you throw from a class that doesn’t have a Request object instance available.

In these cases, the easiest solution is to extend the Slim ErrorMiddleware to wrap the exception in an

and then the standard error handling and rendering will “just work”.

I’m feeling a little lazy, so let’s use an anonymous class to do replace the call to $app->addErrorMiddleware():

Behind the scenes of $app->addErrorMiddleware(), the \Slim\Middleware\ErrorMiddleware is constructed and then added to the middleware stack. We replicate that we an anonymous class that overrides handleException() to wrap the thrown exception if required.

Looking at the code in detail

There’s quite a lot going on here, so let’s break it down into parts.

The constructor to \Slim\Middleware\ErrorMiddleware takes 6 parameters, so when we instantiate, we have to pass them all in though it’s not unusual for the $logger parameter to be left off in the call to $app->addErrorMiddleware(). The easiest way to get a logger instance if there is one, is to grab it from the container where it should be registered under the \Psr\Log\LoggerInterface key which is imported into the file with a use statement.

I've used PHP 8's named arguments as this constructor takes three booleans and it's easier to remember what they do if they are labelled.

We then define the method for our class:

Our anonymous class implements one method, handleException(), which extends the implementation in the parent. We then get to the meat of the method:

The new work our implementation of handleException does is to wrap the thrown exception inside an HttpException. We only want to do this when the needed. One way to determine this is to check the error code; if the thrown exception has a code between 400 and 599, then it's probably an HTTP error code. Of course this is not foolproof, so you may prefer to check that the class implements a particular interface.

To wrap, we instantiate a new HttpException, passing the thrown $exception as the last parameter, so that getPrevious() will find it. Then we set the message to display using setTitle() as that's what's used by the Slim error renderers.

Finally, we call through to our parent's handleException() to do the work with our shiny new HttpException.

We have defined and instantiated the class, so we add it to Slim's middleware stack:

and we're done.

Checking that it works

Putting it all together, if we now throw an exception like this:

then we get the expected HTTP response:

To sum up

The best way to get Slim's error handling system to send the correct HTTP status code when throwing an exception is to thrown an exception that extends Slim's HttpException. Otherwise, you can override Slim's ErrorMiddleware to wrap your exception in an HttpException and you're done.

Thoughts? Leave a reply

Your email address will not be published. Required fields are marked *