我有一个可以抛出异常的laravel控制器,以及一个捕获该异常的全局中间件。在半伪代码中:
// App\Controllers\...
class Controller {
function store() {
throw new FormException; // via validation etc, but it's thrown here
}
}
// App\Http\Middleware\...
class Middleware {
function handle(Closure $next) {
try {
// Breakpoint 1
return $next(); // $response
// Breakpoint 2
}
catch (FormException $ex) {
// Breakpoint 3
exit('FormException caught!');
}
}
}
问题是异常永远不会被捕获。在管道中的Somwhere,应用程序捕获异常并打印一个漂亮的错误页面,但它应该被我的中间件捕获,以便它可以正确处理它。
我可以想象我的中间件没有捕获它的唯一方法是,它是否被捕获到管道内部更深处,而不是进一步向上/周围,但我在其他中间件或管道执行中找不到任何try / catch代码。
这个例外在哪里被捕获?为什么呢?
这可能不是一个很好的模式,但我现在并不关心。我比其他任何事都更好奇。我是否完全误解了Laravel的中间件?
我自己的超级简单中间件测试完成了我的预期:https://3v4l.org/Udr84 - 捕获并处理中间件中的异常。
注意:
$response
对象($next()
的返回值)是已处理的异常页面,因此它已被处理。在哪里以及为什么?App\Exceptions\Handler::render()
中的异常有效,但我希望所有逻辑都在中间件包中,而不是在应用程序代码中。相关的Laravel代码:
Kernel::handle()
启动中间件管道<<这有一个catch-all catch(),但我的catch()是第一个,对吗?Pipeline::then()
启动中间件执行Pipeline::getSlice()
处理并创建$next
闭包答案 0 :(得分:13)
是的,这是从L5.2开始的。抛出异常导致响应被设置为从异常处理程序返回的响应,然后允许中间件从该点退出。
我觉得这很奇怪。可插拔中间件非常适合捕获异常。
还有两种方法可以做到这一点:
App\Exceptions\Handler
中,这不够好,因为包裹无法触及Funky:take the original exception object from the response object:
$response = $next($request);
$exception = $response->exception;
答案 1 :(得分:3)
查看源代码,您需要同时捕获\ Exception和\ Throwable才能使try捕获在中间件中正常工作。这适用于Laravel 5.8
class TryCatchMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
try {
if ( somethingThatCouldThrowAnException() ) {
$request->newVariable = true;
}
} catch (\Exception $e) {
// do nothing
} catch (\Throwable $e) {
// do nothing
}
return $next($request);
}
}
答案 2 :(得分:2)
我遇到了同样的问题。在阅读提到的thread Rudie时,他们提供了一个可行的解决方案,对我有用:
cat $$FileName | sed -e 's/Jingle|heimerscmidt/Jingleheimerscmidt/g'
答案 3 :(得分:0)
如何在不触及App\Exceptions\Handler
文件的情况下捕获错误:
注册CustomExceptionHandler
/* @var ExceptionHandler Illuminate\Contracts\Debug\ExceptionHandler */
$previousHandler = null;
if (app()->bound(ExceptionHandler::class) === true) {
$previousHandler = app()->make(ExceptionHandler::class);
}
app()->singleton(ExceptionHandler::class, function () use ($previousHandler) {
return new CustomExceptionHandler($previousHandler);
});
您的基本CustomExceptionHandler
class CustomExceptionHandler implements ExceptionHandlerInterface
{
/**
* @var ExceptionHandlerInterface|null
*/
private $previous;
public function __construct(ExceptionHandlerInterface $previous = null)
{
$this->previous = $previous;
}
public function report(Exception $exception)
{
$this->previous === null ?: $this->previous->report($exception);
}
public function render($request, Exception $exception)
{
if ($exception instanceof CustomExceptionHandler) {
echo 'This is my particular way to show my errors';
} else {
$response = $this->previous === null ? null : $this->previous->render($request, $exception);
}
return $response;
}
/**
* {@inheritdoc}
*/
public function renderForConsole($output, Exception $exception)
{
/* @var OutputInterface $output */
$this->previous === null ?: $this->previous->renderForConsole($output, $exception);
}
}
答案 4 :(得分:-2)
我想我可以看到为什么你的代码没有捕获异常。请尝试使用以下代码处理句柄方法:
function handle(Closure $next) {
try {
// Breakpoint 1
$response = $next();
// Breakpoint 2
}
catch (FormException $ex) {
// Breakpoint 3
exit('FormException caught!');
}
return $response;
}
上面的代码尚未经过测试,但是当您查看在返回响应之前可以看到的Laravel文档时,您应该执行代码(在这种情况下,您的异常处理逻辑)。请查看:Laravel - Defining Middleware了解有关Before& amp;中间件定义后。
顺便说一句,还要看看这个文件:Laravel / app / Exceptions / Handler.php,我相信这是一个更好的地方来处理你的全球异常。