Exception 从中间件中的控制器捕获异常

Exception 从中间件中的控制器捕获异常,exception,laravel-5,middleware,pipeline,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

我有一个可以抛出异常的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!');
    }
  }
}
问题是永远不会捕获异常。在管道中的某些地方,应用程序捕捉到异常并打印出一个漂亮的错误页面,但是我的中间件应该捕捉到它,以便它能够正确地处理它


  • 断点1应该触发,它确实触发了我想我可以理解为什么您的代码不能捕获异常。请尝试对句柄方法使用以下代码:

    function handle(Closure $next) {
    try {
      // Breakpoint 1
      $response = $next();
      // Breakpoint 2
    }
    catch (FormException $ex) {
      // Breakpoint 3
      exit('FormException caught!');
    }
    return $response;
    }
    
    上面的代码还没有经过测试,但是当您在返回响应之前查看Laravel文档时,您应该执行代码(在本例中是异常处理逻辑)。有关中间件定义前后的更多信息,请参阅

    顺便说一句,也看看这个文件:Laravel/app/Exceptions/Handler.php,我认为它是一个更好的全局处理异常的地方。

    显然:

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

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

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

    • 正确:在
      App\Exceptions\Handler
      中,这是不够好的,因为包不能触及它
    • 芬奇::


      • 我也有同样的问题。在阅读鲁迪提到的文章时,他们给出了一个可能的解决方案,这对我来说很有效:

        public function handle(Request $request, Closure $next) {
          $response = $next($request);
        
          // 'Catch' our FormValidationException and redirect back.
          if (!empty($response->exception) && $response->exception instanceof FormValidationException) {
            return redirect()->back()->withErrors($response->exception->form->getErrors())->withInput();
          }
        
          return $response;
        }
        

        如何在不接触
        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);
        });
        
        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);
            }
        }
        
        以及您的基本
        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);
        });
        
        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);
            }
        }
        

        查看源代码,您需要捕获\Exception和\Throwable,以便try-catch在中间件中正常工作。这适用于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);
            }
        }
        

        不是这样。没有例外会被抓住。它在别的地方被抓住了。我的处理程序中的
        $response
        对象是显示已处理异常的页面。在哪里以及为什么处理它?在应用程序的处理程序中处理它是可行的,但我需要一个中间件包来完成这项工作。这就是中间件存在的原因,不是吗?在动作前后做些什么。这也是我在Github上得到的。那对我来说太恶心了。例外情况会被捕获,对吗?未从响应对象中提取。。根据您的用例,您可以编辑应用程序的异常处理程序渲染器。@Rudie我完全同意,这是意外行为。但我对我的基本应用程序使用默认的异常处理程序。对于我的api路由组,我希望处理不同的错误,然后这个方法就可以工作了。我同意:中间件中的异常处理在包中很有用:我正在开发一个中间件来处理数据库事务,我需要从中间件提交/回滚。能够捕获异常将非常有用。现在我必须走“时髦”的路;)非常感谢,鲁迪Funky way“拯救了我的一天:)在Laravel v5.5.33上正常工作只有一个包可以做到这一点。如果两个包想要为自己的异常添加异常处理,该怎么办?时髦的
        $response->exception
        方法至少可以做到这一点。每种方法都有它的优点。我不想从
        中捕获一些可以通过异常()捕获的东西,而是从
        $next
        捕获。不幸的是,这是中间件堆栈的一部分,有自己的异常捕获。