使用自定义异常处理程序创建Laravel404页面

来自菜鸟教程
跳转至:导航、​搜索

介绍

当发生前所未有的事件或错误时,会引发 PHP 异常。 根据经验,不应使用异常来控制应用程序逻辑,例如 if 语句,并且应该是 Exception 类的子类。

作为史无前例的,我们的应用程序的任何时间点或时间都可能引发异常。

Laravel 提供了一个方便的异常处理程序类,它检查 Laravel 应用程序中抛出的所有异常并给出相关响应。 这是因为 Laravel 中使用的所有异常都扩展了 Exception 类。

让单个类捕获所有异常的一个主要优点是,我们能够创建自定义异常处理程序,根据异常返回不同的响应消息。

在本教程中,我们将了解如何在 Laravel 5.2 中创建自定义异常处理程序,以及如何根据异常返回 404 页面。

这个怎么运作

在 Laravel 5.2 中,自定义和默认的所有错误和异常都由 app/Exceptions/Handler.php 中的 Handler 类借助两种方法处理。

  • report()

report 方法使您能够记录引发的异常或将它们解析到错误记录引擎,例如 bugsnagsentry,我们不会在本教程中深入研究。

  • render()

render 方法以异常引发的错误消息进行响应。 它从异常中生成 HTTP 响应并将其发送回浏览器。

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $e
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $e)
    {
        return parent::render($request, $e);
    }

但是,我们可以使用我们自己的自定义异常处理程序覆盖默认错误处理。

/**
 * @param  \Illuminate\Http\Request  $request
 * @param  \Exception  $e
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $e)
{
    if ($e instanceof CustomException) {
        return response()->view('errors.custom', [], 500);
    }

    return parent::render($request, $e);
}

在底层,Laravel 会进行自己的处理检查以确定对异常的最佳响应。 看一下父类(Illuminate\Foundation\Exceptions\Handler),render 方法会根据抛出的异常生成不同的响应。

    /**
     * Render an exception into a response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $e
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function render($request, Exception $e)
    {
        if ($e instanceof HttpResponseException) {
            return $e->getResponse();
        } elseif ($e instanceof ModelNotFoundException) {
            $e = new NotFoundHttpException($e->getMessage(), $e);
        } elseif ($e instanceof AuthenticationException) {
            return $this->unauthenticated($request, $e);
        } elseif ($e instanceof AuthorizationException) {
            $e = new HttpException(403, $e->getMessage());
        } elseif ($e instanceof ValidationException && $e->getResponse()) {
            return $e->getResponse();
        }

        if ($this->isHttpException($e)) {
            return $this->toIlluminateResponse($this->renderHttpException($e), $e);
        } else {
            return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
        }
    }

抛出一个 Laravel Eloquent 异常

在本节中,我们将通过故意引发异常来创建内置的 Laravel 错误。

为此,我们将尝试使用 firstOrFail() Eloquent 方法从模型中获取不存在的记录。

继续设置一个简单的 SQLite 数据库。 幸运的是,Laravel 附带了 User 模型和相应的用户表。 只需执行以下操作。

  1. 创建一个新的 Laravel 项目。
  2. 更新您的 .env 文件,使 DB_CONNECTION 成为 sqlite 和唯一的数据库参数。
  3. 在数据库目录中创建一个 database.sqlite 文件。 这是 config/database.php 中配置的默认 SQLite 数据库
  4. 在 Laravel 项目的路径上运行 php artisan migrate。 这将在数据库中建立一个用户表。

然后我们将添加一个路由和一个控制器来获取用户表中的第一个用户,而该用户恰好不存在。

应用程序/Http/routes.php

Route::get('/user', [
    'uses' => 'SampleController@findUser',
    'as' => 'user'
]);

应用程序/Http/Controllers/SampleController.php

     /**
     * Return the first user in the users table
     *
     * @return Array    User details
     */
    public function findUser()
    {
        $user = User::firstOrFail();
        return $user->toArray();
    }

在浏览器上运行它会返回一个 ModelNotFoundException 错误响应。

使用自定义处理程序捕获异常

ModelNotFoundException

有了这个例外,我们现在可以添加一个自定义处理程序来返回我们自己的错误消息。

如果异常是 ModelNotFoundExceptionNotFoundHttpException

如果两者都不是,我们将让 Laravel 处理异常。

   /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $e
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $e)
    {
        //check if exception is an instance of ModelNotFoundException.
        if ($e instanceof ModelNotFoundException) {
            // ajax 404 json feedback
            if ($request->ajax()) {
                return response()->json(['error' => 'Not Found'], 404);
            }

            // normal 404 view page feedback
            return response()->view('errors.missing', [], 404);
        }

        return parent::render($request, $e);
    }

resources/view/errors 中添加一个 404.blade.php 文件以包含我们的用户反馈。

<!DOCTYPE html>
<html>
<head>
    <title>User not found.</title>
</head>
<body>
    <p>You broke the balance of the internet</p>
</body>
</html>

如果我们现在刷新页面,我们会在视图中看到以下带有 404 状态代码的消息。

NotFoundHttpException

当用户访问诸如 /foo/bar/randomstr1ng 之类的未定义路由时,会抛出一个 NotFoundHttpException 异常,它是 Symfony 包的一部分。

为了处理这个异常,我们将在我们之前修改的 render 方法中添加第二个条件,并从 resources/view/errors/missing.blade.php 返回一条消息

/**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $e
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $e)
    {
        //check if exception is an instance of ModelNotFoundException.
        //or NotFoundHttpException
        if ($e instanceof ModelNotFoundException or $e instanceof NotFoundHttpException) {
            // ajax 404 json feedback
            if ($request->ajax()) {
                return response()->json(['error' => 'Not Found'], 404);
            }

            // normal 404 view page feedback
            return response()->view('errors.missing', [], 404);
        }

        return parent::render($request, $e);
    }

利用 Laravel 的 Abort 方法

就像我们在上一节中所做的那样,Laravel 5.2 使得根据抛出的异常创建自定义错误页面变得非常容易。

我们还可以通过调用带有可选响应消息的 abort 方法来简单地生成 404 错误页面响应。

abort(404, 'The resource you are looking for could not be found');

这将检查相应的 resources/view/errors/404.blade.php 并将带有 404 状态代码的 HTTP 响应返回给浏览器。 这同样适用于 401 和 500 错误状态代码。

结论

根据应用程序的环境,您可能希望显示不同级别的错误详细信息。 您可以通过在 .env 文件中更改 config/app.php 中的 APP_DEBUG 值来将其设置为 true 或 false。

在大多数情况下,您可能不希望生产中的用户看到详细的错误消息。 因此,在生产环境中将 APP_DEBUG 值设置为 false 是一种很好的做法。