Laravel:在哪里抛出HTTP异常



>Background

在 PHP/Laravel MVC 应用程序中,响应代码和正文通常由抛出的异常决定。如果抛出 HTTP 异常(继承自SymfonyComponentHttpKernelExceptionHttpException(,则会抛出正确的响应代码(在某些情况下会抛出 JSON 响应(。还可以引发其他类型的非 http 相关异常。

问题

应该在哪里抛出 HTTP 异常?

  • A仅控制器
  • B任何地方。应用程序堆栈中的深层或浅层。

我应该在控制器中捕获异常并抛出这些异常的 HTTP 版本吗?或者我应该在服务类、存储库或实用程序的深处抛出 HTTP 异常,考虑到 99% 的 MVC 框架应用程序无论如何都基于 HTTP 请求>>响应生命周期?

我的回答不是针对Laravel的,因为我觉得以框架思维方式工作实际上违背了你最初的问题。

始终引发定制的异常,然后在控制器中处理转换。在这种情况下,请将其包裹在HttpException中。这有几个很好的理由:

  • 决定将哪些状态代码和消息委托给实现(在本例中为与框架集成(。这意味着您可以将代码放在任何框架中并单独处理其错误。
  • 您决定需要一个 CLI 命令/工作线程,现在您的服务抛出的HttpException对您的 CLI 命令毫无意义。

本质上考虑计算器,它会抛出DivisionByZeroException.对于控制器,您可以将其包装在HttpException400 BAD REQUEST中并重新抛出。对于 CLI,您的命令可以让异常呈现在屏幕上Division By Zero.无论哪种方式,此决定都不是由你的服务做出的。

HTTP 异常应该在哪里抛出?

虽然这通常取决于偏好,但框架本身似乎对此采取了固执己见的立场,您应该将它们扔到任何地方。事实上,Laravel有一些有用的助手,可以更轻松地抛出带有相关响应代码的异常:

abort(403, "Exception message"); //Will throw an HTTP exception with code 403
abort_if(true, 400, "Condition failed"); //Will throw a 400 error if the first parameter is true
abort_unless(false, 422, "Condition failed"); //Will throw a 422 error if the first parameter is false

实例:

public function getById($id) { 
$model = Model::find($id);
//These are equivalent 
if ($model == null) {
abort(404, "$id not found");
}
abort_if($model == null, 404, "$id not found");  
abort_unless($model != null, 404, "$id not found");
}

这在手册的错误处理部分有所涉及

请注意,abort确实会引发 HTTP 异常,因此您仍然可以捕获它们并在需要时处理它们。

对这个问题似乎存在普遍的误解。我的理解是,问题是应该在哪里抛出HTTP异常,但它正在演变为HTTP上下文中更通用的异常处理。

首先,如果你有一个 HTTP 异常,这意味着一个仅在 HTTP 请求/响应周期上下文中有意义的异常,那么你应该能够把它扔到它发生的地方,而不是抛出其他东西,目的是在它到达控制器时转换它,这就是abort助手要做的事情。

但是,如果您有异常(任何类型的异常(,在未处理时应使用特定的 http 响应代码进行解释,您可以选择处理它:

  1. 使该异常继承自 SymfonyHttpException(这可能会让人感觉有点奇怪,因为一个完全正常的异常继承自一个在请求/响应生命周期之外没有意义的类(。
  2. 在您的异常中实现render方法,例如:

    class SpecialException extends Exception { 
    public function render() {
    return response()->view('errors.403', [], 403);
    }
    }
    
  3. 在 \App\Exceptions\Handler 中具有特定的处理行为,例如:

    class Handler {
    // ....
    public function render($request, $exception) {
    if ($exception instanceof SpecialException) {
    return response()->view('errors.403', [], 403);
    }
    return parent::render()
    }
    }
    

最新更新