framework: Can't catch exception in middleware
I want a middleware package to catch an exception in its middleware.
- Exception: FormValidationException
- Thrown by a validator helper, or app logic, in the controller
- Middleware: HandleFormValidation
- Catches only that exception and does a redirect with data & errors etc
But the exception can’t be caught by the middleware, because it’s already been caught and handled somewhere else. The $response
object ($next()
return value) is an exception error page.
Who catches my exception, and where, and why so early? Aren’t exceptions allowed in the middleware pipeline?
The app can extend its App\Exceptions\Handler
to ‘render’ that exception into a redirect, but I want all logic to be in the package, not just half.
Middleware looks like this:
public function handle(Request $request, Closure $next) {
try {
return $next($request); // Passes through an uncaught exception error page
}
catch (\Exception $ex) {
exit(__METHOD__); // Never gets here
}
}
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 8
- Comments: 20 (7 by maintainers)
What a crappy framework!!!
We don’t recommend you write try catch blocks in middleware. Instead, handle exceptions inside the exception handler. Perhaps https://github.com/GrahamCampbell/Laravel-Exceptions would be of use to you?
Yeah, I don’t like that, because only 1 party can extend a base class. What if 4 packages want specific exception handling? That’s why middleware is perfect.
This does the trick, but it feels really funky:
Is that what you recommend? It can’t be in
App\Exceptions\Handler
, because that’s app logic.Thanks for super fast replies!
I’m still curious how this handling works though. I was trying to debug the middleware pipeline, but I couldn’t find any exception catching. I really thought my middleware was the first to catch it. Where does that happen?
Hi! The correct way is don’t catch errors directly on middleware. You need add a custom ExceptionHandler. If you like register handler on your middleware, you can do it, but forget try/catch.
How catch errors without touching
App\Exceptions\Handler
file:Register your
CustomExceptionHandler
And your basic
CustomExceptionHandler
Back out from that point how? Where is my exception? How can non-app code do something with specific exceptions? Do I really extract it from the response object? That seems silly…
Yes, this is the beavhiour starting from L5.2. Throwing an exception causes the response to be set as that returned from the exception handler, and then the middleware is allowed to backout from that point.
I guess it would be conventional for the application to still handle package exceptions. Feel free to ask around on the forums for solutions other people might have.
In terms of where does this happen, it happens in our pipeline. Starting with L5.2, there’s a custom http pipeline: https://github.com/laravel/framework/blob/5.2/src/Illuminate/Routing/Pipeline.php.
@Cleanshooter While it’s not possible to use try / catch in a middleware, it’s indeed possible to dictate the response with throwing an exception or using the abort() helper:
@ivorobioff I will be happy to work with your “not crappy” framework anytime soon
The trouble is, I think Middleware follows the Chain of Responsibility pattern, similar to a Handler-Stack, by way of a series of nested Closures. I’m not really sure how to interrupt that flow, given that you can’t exactly write an override class for a Closure that could try-catch the error, and more importantly, have an awareness of what the next link in the chain is. Middleware classes don’t extend a common base-class for you to leverage either. So the only thing I could think of would be to take a step further back from that, to how Middleware gets stored and executed in the first place. Have something that wraps each closure in a closure of your own that’s designed to handle the errors and then move on to the next one.
Current flow:
Your flow:
I’m not really sure where what class you’d have to override to do this. I think I’d start by dissecting the Kernel, or using debug_backtrace inside of one of the Middleware classes to see what fires it. Best of luck. And please let me know how it turns out.