从中间件

时间:2016-07-30 23:06:08

标签: exception laravel-5 middleware pipeline

我有一个可以抛出异常的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,应用程序捕获异常并打印一个漂亮的错误页面,但它应该被我的中间件捕获,以便它可以正确处理它。

  • 断点1应该触发,它确实<<好
  • 断点2不应该触发,并且它不会<<好
  • 断点3应该触发,但它不会<<什么??

我可以想象我的中间件没有捕获它的唯一方法是,它是否被捕获到管道内部更深处,而不是进一步向上/周围,但我在其他中间件或管道执行中找不到任何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闭包

5 个答案:

答案 0 :(得分:13)

显然是this is by design

  

是的,这是从L5.2开始的。抛出异常导致响应被设置为从异常处理程序返回的响应,然后允许中间件从该点退出。

我觉得这很奇怪。可插拔中间件非常适合捕获异常。

还有两种方法可以做到这一点:

答案 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,我相信这是一个更好的地方来处理你的全球异常。