首页 > 文章列表 > Laravel中的异常处理

Laravel中的异常处理

laravel 异常处理 错误处理
276 2023-08-29

在本文中,我们将探讨 Laravel Web 框架最重要且讨论最少的功能之一——异常处理。 Laravel 附带了一个内置的异常处理程序,允许您以友好的方式轻松报告和呈现异常。

在本文的前半部分,我们将探讨异常处理程序提供的默认设置。事实上,我们首先会通过默认的 Handler 类来了解 Laravel 如何处理异常。

在本文的后半部分,我们将继续了解如何创建自定义异常处理程序来捕获自定义异常。

设置先决条件

在我们直接深入了解处理程序类之前,让我们先看一下与异常相关的几个重要配置参数。

继续打开config/app.php 文件。让我们仔细看看下面的代码片段。

...
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/

'debug' => (bool) env('APP_DEBUG', false),
...
...

顾名思义,如果将其设置为 TRUE,它将帮助您调试应用程序生成的错误。该变量的默认值设置为 .env 文件中 APP_DEBUG 环境变量的值。

在开发环境中,您应该将其设置为 TRUE,以便您可以轻松跟踪错误并修复错误。另一方面,您希望在生产环境中将其关闭,在这种情况下它将显示一般错误页面。

记录配置文件

除了显示错误之外,Laravel 还允许您在日志文件中记录错误。让我们快速浏览一下可用于日志记录的选项。让我们访问 config/logging.php 文件并仔细查看以下代码片段。

<?php

use MonologHandlerNullHandler;
use MonologHandlerStreamHandler;
use MonologHandlerSyslogUdpHandler;

return [

    /*
    |--------------------------------------------------------------------------
    | Default Log Channel
    |--------------------------------------------------------------------------
    |
    | This option defines the default log channel that gets used when writing
    | messages to the logs. The name specified in this option should match
    | one of the channels defined in the "channels" configuration array.
    |
    */

    'default' => env('LOG_CHANNEL', 'stack'),

    /*
    |--------------------------------------------------------------------------
    | Log Channels
    |--------------------------------------------------------------------------
    |
    | Here you may configure the log channels for your application. Out of
    | the box, Laravel uses the Monolog PHP logging library. This gives
    | you a variety of powerful log handlers / formatters to utilize.
    |
    | Available Drivers: "single", "daily", "slack", "syslog",
    |                    "errorlog", "monolog",
    |                    "custom", "stack"
    |
    */

    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['single'],
            'ignore_exceptions' => false,
        ],

        'single' => [
            'driver' => 'single',
            'path' => storage_path('logs/laravel.log'),
            'level' => 'debug',
        ],

        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => 'debug',
            'days' => 14,
        ],

        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel Log',
            'emoji' => ':boom:',
            'level' => 'critical',
        ],

        'papertrail' => [
            'driver' => 'monolog',
            'level' => 'debug',
            'handler' => SyslogUdpHandler::class,
            'handler_with' => [
                'host' => env('PAPERTRAIL_URL'),
                'port' => env('PAPERTRAIL_PORT'),
            ],
        ],

        'stderr' => [
            'driver' => 'monolog',
            'handler' => StreamHandler::class,
            'formatter' => env('LOG_STDERR_FORMATTER'),
            'with' => [
                'stream' => 'php://stderr',
            ],
        ],

        'syslog' => [
            'driver' => 'syslog',
            'level' => 'debug',
        ],

        'errorlog' => [
            'driver' => 'errorlog',
            'level' => 'debug',
        ],

        'null' => [
            'driver' => 'monolog',
            'handler' => NullHandler::class,
        ],

        'emergency' => [
            'path' => storage_path('logs/laravel.log'),
        ],
    ],

];

Laravel 日志记录是基于通道的。每个通道提供了不同的日志信息写入方式。例如,single 通道将日志写入日志文件,slack 通道用于将日志发送到 Slack 以通知您的团队。由于 Laravel 使用 Monolog PHP 库进行日志记录,因此您应该在该库的上下文中设置上述选项。默认情况下,Laravel 使用 stack 通道来记录消息。

默认日志文件位于storage/logs/laravel.log,在大多数情况下就足够了。另一方面,通道配置中的 level 属性设置为一个值,该值指示将记录的错误的严重性。

Handler

接下来,我们来看看默认 Laravel 应用程序附带的默认 Handler 类。继续并打开 app/Exceptions/Handler.php 文件。

<?php

namespace AppExceptions;

use IlluminateFoundationExceptionsHandler as ExceptionHandler;
use Throwable;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];

    /**
     * Report or log an exception.
     *
     * @param  Throwable  $exception
     * @return void
     *
     * @throws Throwable
     */
    public function report(Throwable $exception)
    {
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Throwable  $exception
     * @return SymfonyComponentHttpFoundationResponse
     *
     * @throws Throwable
     */
    public function render($request, Throwable $exception)
    {
        return parent::render($request, $exception);
    }
}

处理程序类负责两个重要的功能:报告和呈现所有错误。

让我们仔细看看 report 方法。

/**
 * Report or log an exception.
 *
 * @param  Throwable  $exception
 * @return void
 *
 * @throws Throwable
 */
public function report(Throwable $exception)
{
    parent::report($exception);
}

report 方法用于将错误记录到日志文件中。同时,还需要注意 dontReport 属性,该属性列出了不应记录的所有类型的异常。

接下来,让我们引入 render 方法。

/**
 * Render an exception into an HTTP response.
 *
 * @param  IlluminateHttpRequest  $request
 * @param  Throwable  $exception
 * @return SymfonyComponentHttpFoundationResponse
 *
 * @throws Throwable
 */
public function render($request, Throwable $exception)
{
    return parent::render($request, $exception);
}

如果 report 方法用于记录或报告错误,则 render 方法用于在屏幕上呈现错误。事实上,这个方法处理的是异常发生时向用户显示的内容。

render 方法还允许您自定义不同类型异常的响应,我们将在下一节中看到。

自定义异常类

在本节中,我们将创建一个自定义异常类,用于处理 CustomException 类型的异常。创建自定义异常类背后的想法是轻松管理自定义异常并同时呈现自定义响应。

继续创建一个包含以下内容的文件app/Exceptions/CustomException.php

<?php
 
namespace AppExceptions;
 
use Exception;
 
class CustomException extends Exception
{
    /**
     * Report the exception.
     *
     * @return void
     */
    public function report()
    {
    }
 
    /**
     * Render the exception into an HTTP response.
     *
     * @param  IlluminateHttpRequest
     * @return IlluminateHttpResponse
     */
    public function render($request)
    {
        return response()->view(
                'errors.custom',
                array(
                    'exception' => $this
                )
        );
    }
}

这里需要注意的重要一点是 CustomException 类必须扩展核心 Exception 类。出于演示目的,我们仅讨论 render 方法,但当然您也可以自定义报告方法。

如您所见,在我们的案例中,我们将用户重定向到 errors.custom 错误页面。通过这种方式,您可以针对特定类型的异常实现自定义错误页面。

当然,我们需要在 resources/views/errors/custom.blade.php 创建一个关联的视图文件。

Exception details: <b>{{ $exception->getMessage() }}</b>

这是一个非常简单的视图文件,显示错误消息,但当然您可以按照您想要的方式设计它。

我们还需要对 app/Exceptions/Handler.php 文件的 render 方法进行更改,以便可以调用我们的自定义异常类。让我们将 render 方法替换为 app/Exceptions/Handler.php 文件中的以下内容。

/**
 * Render an exception into an HTTP response.
 *
 * @param  IlluminateHttpRequest  $request
 * @param  Throwable  $exception
 * @return SymfonyComponentHttpFoundationResponse
 *
 * @throws Throwable
 */
public function render($request, Throwable $exception)
{
    if ($exception instanceof AppExceptionsCustomException)  {
        return $exception->render($request);
    }

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

如您所见,我们首先检查 render 方法中的异常类型。如果异常的类型是 AppExceptionsCustomException,我们调用该类的 render 方法。

如何测试我们的 CustomException

现在一切都已就位。接下来,让我们继续在 app/Http/Controllers/ExceptionController.php 创建一个控制器文件,以便我们可以测试我们的自定义异常类。

<?php
namespace AppHttpControllers;
 
use AppHttpControllersController;
 
class ExceptionController extends Controller
{
    public function index()
    {
        // something went wrong and you want to throw CustomException
        throw new AppExceptionsCustomException('Something Went Wrong.');
    }
}

当然,您需要在routes/web.php中添加关联的路由,如以下代码片段所示。

// Exception routes
Route::get('exception/index', 'ExceptionController@index');

完成后,您可以访问 https://your-laravel-site.com/exception/index URL 来查看它是否按预期工作。它应该根据我们的配置显示 errors.custom 视图。

这就是 Laravel 中处理自定义异常的方法。

结论

今天,我们了解了 Laravel 中的异常处理功能。在文章的开头,我们探索了 Laravel 提供的基本配置,以便渲染和报告异常。此外,我们还简要了解了默认的异常处理程序类。

在文章的后半部分,我们准备了一个自定义异常处理程序类,演示了如何在应用程序中处理自定义异常。