2016-07-30 78 views
4

我有一个可以抛出异常的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()启动中间件管道< <这一个包罗万象的捕捞(),但我赶上()是第一位的,对不对?
  • Pipeline::then()启动中间件执行
  • Pipeline::getSlice()手柄和创建$next关闭

回答

11

显然this is by design

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

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

两种方式是怎么做的:

+0

我同意:中间件中的异常处理在包中是有用的:我正在开发一个中间件来处理数据库事务,我需要从中间件提交/回滚。能够捕捉异常会非常有用。现在我必须走'时髦'的方式;) – Moppo

+0

非常感谢Rudie。 '** Funky way **'拯救了我的一天:)在Laravel ** v5.5.33 **上正确运行** –

-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以获取更多信息之前&中间件定义之后。

顺便说一句,也看看这个文件:Laravel/app/Exceptions/Handler.php,我认为这是一个更好的地方来处理你的例外全球。

+0

不是这样。没有例外可以被捕获。它被抓到别的地方了。我的处理程序中的$ response对象是显示处理的异常的页面。在哪里和为什么处理? – Rudie

+0

在应用程序的处理程序中处理它,但我想要一个中间件程序包来执行此操作。这就是中间件存在的原因,不是吗?在行动之前和之后做些事情。 – Rudie

2

我有同样的问题。当读取thread Rudie提到,他们给一个可能的解决方案有哪些工作对我来说:

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; 
} 
+0

这就是我在Github上获得的。这对我来说太讨厌了。例外情况会被发现,对吧?不从Response对象中提取。根据您的用例,您可以编辑App的异常处理程序渲染器。 – Rudie

+0

@Rudie我完全同意,这是意想不到的行为。但是我为我的基础应用程序使用默认的异常处理程序。对于我的api路线组,我想处理不同的错误,那么这个方法就行得通。 – Jetse

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); 
    } 
} 
+0

只有1个包可以做到这一点。如果2个包想为自己的异常添加异常处理呢?时髦的'$ response-> exception'方法至少可以做到这一点。每个都有它的优势。 – Rudie