Manually thrown exceptions do not traverse the middleware stack


#1

I’ve been playing around with my error handling and noticed a little while ago that my middleware-set headers were being reset when errors were thrown. Playing around a bit more, it seems that it’s only exceptions that are manually thrown.

Besides repeating my middleware in my error handling, is there any way to make thrown exceptions still traverse the middleware stack? It makes things quite complicated when it comes to setting headers, logging, etc.

Edit: for not found and method not allowed only is this true. For php errors and exceptions thrown during the script it also does not traverse the middleware.


#2

If i understand what you’re doing is use Exception for build an HTTP Response (at the end of the handler) right?

I suggest you to DO NOT USE this approach, and use only a default http response at the end (if you need it).
You should return a Response object that will be handled by the middleware stack instead of an Exception.

imo your design has this issue, try thinking that exception should be handled, not used for build response.
You can catch it, build a response and return it, because Exception != Response.


#3

So instead of throwing exceptions that return a response I should be crafting the HTTP response inside my code when, for example, a resource is not found, and return that instead? I was under the impression that the purpose of error handling was for this reason, but I’m happy to do it the way you’ve suggested if it’s more correct.

However, this does still pose the same issue for exceptions that are thrown by PHP, PDO etc. These exceptions do not traverse back up the middleware stack so I need to repeat my middleware code in my error handlers (I.e CORS headers).


#4

You should wrap all the “\Exception”.
Example

class UserRepository
{
    private $pdo;

    public function __construct(PDO $pdo)
    {
           $this->pdo = $pdo;
    }

     public function add(User $user)
     {
         try{
             $this->pdo->insertQueryHere($user); // Here you do your job
         } catch (\Exception $e){
             throw new MyCustomException('ERROR OCCURED SAVING USER! LOL!!', 'YOUR_CODE', $e);
         }
     }
}

So you can easily handle it.


#5

Thanks for that code snippet.

I think I was a bit confused by your first reply. What you showed in the code snippet is what I do now (admittedly not for all exceptions - I wasn’t doing it for PDO exceptions, so I have one improvement I can make).

However, my issue is that the response I return at the end of my error handlers does not go through the middleware. To give you an example, I’m using the error handler snippet from the Slim docs:

$c['errorHandler'] = function ($c) {
    return function ($request, $response, $exception) use ($c) {
        return $c['response']->withStatus(500)
             ->withHeader('Content-Type', 'text/html')
             ->write('Something went wrong!');
    };
};

I also have a middleware from the Slim docs to add CORS headers to each response. This works fine when a response is returned within the normal flow of my application, and also in the notFoundHandler and notAllowedHandler if thrown by Slim - the CORS headers are added to my response object. However, in the phpErrorHandler and errorHandler, and for any exceptions I throw in my code that make it to the error handler, the middleware is not executed when the response is returned.


#6

Sorry for the confusing reply, you should wrap all the possible exception, and NEVER throw a generic “\Exception”.
And wrapping all the errors you can also handle the response via middleware.


#7

So if I need my response to be handled via the middleware, I should catch the exception and return my response within my catch block instead of letting the exception bubble up to the error handler and returning my response there?


#8

In a clean design this is imho the right way.

If you want to “hack it” you can put logic for process the middleware in the errorHandler, but this is NOT the right way.

You’re just making things harder. :smiley:


#9

Things are much clearer now. Thanks for your help!