GVKun编程网logo

Laravel 核心解读 -- 异常处理(laravel哪个类处理异常)

30

针对Laravel核心解读--异常处理和laravel哪个类处理异常这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展Laravel中的异常处理、laravel异常处理、Laravel源码核心

针对Laravel 核心解读 -- 异常处理laravel哪个类处理异常这两个问题,本篇文章进行了详细的解答,同时本文还将给你拓展Laravel 中的异常处理、laravel 异常处理、Laravel 源码核心解读:全局异常处理、laravel 请求异常处理等相关知识,希望可以帮助到你。

本文目录一览:

Laravel 核心解读 -- 异常处理(laravel哪个类处理异常)

Laravel 核心解读 -- 异常处理(laravel哪个类处理异常)

<p> 异常处理是编程中十分重要但也最容易被人忽视的语言特性,它为开发者提供了处理程序运行时错误的机制,对于程序设计来说正确的异常处理能够防止泄露程序自身细节给用户,给开发者提供完整的错误回溯堆栈,同时也能提高程序的健壮性。</p> <p > 这篇文章我们来简单梳理一下 Laravel 中提供的异常处理能力,然后讲一些在开发中使用异常处理的实践,如何使用自定义异常、如何扩展 Laravel 的异常处理能力。</p> <h3 > 注册异常 Handler</h3> <p > 这里又要回到我们说过很多次的 Kernel 处理请求前的 bootstrap 阶段,在 bootstrap 阶段的 < code>Illuminate\Foundation\Bootstrap\HandleExceptions</code> 部分中 Laravel 设置了系统异常处理行为并注册了全局的异常处理器:</p>


class HandleExceptions
{
    public function bootstrap(Application $app)
    {
        $this-&gt;app = $app;

        error_reporting(-1);

        set_error_handler([$this, ''handleError'']);

        set_exception_handler([$this, ''handleException'']);

        register_shutdown_function([$this, ''handleShutdown'']);

        if (! $app-&gt;environment(''testing'')) {
            ini_set(''display_errors'', ''Off'');
        }
    }
    
    
    public function handleError($level, $message, $file = '''', $line = 0, $context = [])
    {
        if (error_reporting() &amp; $level) {
            throw new ErrorException($message, 0, $level, $file, $line);
        }
    }
}

<p><code>set_exception_handler ([$this, ''handleException''])</code > 将 < code>HandleExceptions</code > 的 < code>handleException</code > 方法注册为程序的全局处理器方法:</p>


public function handleException($e)
{
    if (! $e instanceof Exception) {
        $e = new FatalThrowableError($e);
    }

    $this-&gt;getExceptionHandler()-&gt;report($e);

    if ($this-&gt;app-&gt;runningInConsole()) {
        $this-&gt;renderForConsole($e);
    } else {
        $this-&gt;renderHttpResponse($e);
    }
}

protected function getExceptionHandler()
{
    return $this-&gt;app-&gt;make(ExceptionHandler::class);
}

// 渲染CLI请求的异常响应
protected function renderForConsole(Exception $e)
{
    $this-&gt;getExceptionHandler()-&gt;renderForConsole(new ConsoleOutput, $e);
}

// 渲染HTTP请求的异常响应
protected function renderHttpResponse(Exception $e)
{
    $this-&gt;getExceptionHandler()-&gt;render($this-&gt;app[''request''], $e)-&gt;send();
}

<p> 在处理器里主要通过 < code>ExceptionHandler</code > 的 < code>report</code > 方法上报异常、这里是记录异常到 < code>storage/laravel.log</code > 文件中,然后根据请求类型渲染异常的响应生成输出给到客户端。这里的 ExceptionHandler 就是 < code>\App\Exceptions\Handler</code > 类的实例,它是在项目最开始注册到服务容器中的:</p>


// bootstrap/app.php

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
*/

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.''/../'')
);

/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
*/
......

$app-&gt;singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

<p> 这里再顺便说一下 < code>set_error_handler</code > 函数,它的作用是注册错误处理器函数,因为在一些年代久远的代码或者类库中大多是采用 PHP 那件函数 < code>trigger_error</code > 函数来抛出错误的,异常处理器只能处理 Exception 不能处理 Error,所以为了能够兼容老类库通常都会使用 < code>set_error_handler</code > 注册全局的错误处理器方法,在方法中捕获到错误后将错误转化成异常再重新抛出,这样项目中所有的代码没有被正确执行时都能抛出异常实例了。</p>


/**
 * Convert PHP errors to ErrorException instances.
 *
 * @param  int  $level
 * @param  string  $message
 * @param  string  $file
 * @param  int  $line
 * @param  array  $context
 * @return void
 *
 * @throws \ErrorException
 */
public function handleError($level, $message, $file = '''', $line = 0, $context = [])
{
    if (error_reporting() &amp; $level) {
        throw new ErrorException($message, 0, $level, $file, $line);
    }
}

<h3> 常用的 Laravel 异常实例 </h3> <p><code>Laravel</code > 中针对常见的程序异常情况抛出了相应的异常实例,这让开发者能够捕获这些运行时异常并根据自己的需要来做后续处理(比如:在 catch 中调用另外一个补救方法、记录异常到日志文件、发送报警邮件、短信)</p> <p > 在这里我列一些开发中常遇到异常,并说明他们是在什么情况下被抛出的,平时编码中一定要注意在程序里捕获这些异常做好异常处理才能让程序更健壮。</p> <ul> <li> <code>Illuminate\Database\QueryException</code> Laravel 中执行 SQL 语句发生错误时会抛出此异常,它也是使用率最高的异常,用来捕获 SQL 执行错误,比方执行 Update 语句时很多人喜欢判断 SQL 执行后判断被修改的行数来判断 UPDATE 是否成功,但有的情景里执行的 UPDATE 语句并没有修改记录值,这种情况就没法通过被修改函数来判断 UPDATE 是否成功了,另外在事务执行中如果捕获到 QueryException 可以在 catch 代码块中回滚事务。</li> <li> <code>Illuminate\Database\Eloquent\ModelNotFoundException</code> 通过模型的 < code>findOrFail</code > 和 < code>firstOrFail</code > 方法获取单条记录时如果没有找到会抛出这个异常 (<code>find</code > 和 < code>first</code > 找不到数据时会返回 NULL)。</li> <li> <code>Illuminate\Validation\ValidationException</code> 请求未通过 Laravel 的 FormValidator 验证时会抛出此异常。</li> <li> <code>Illuminate\Auth\Access\AuthorizationException</code> 用户请求未通过 Laravel 的策略(Policy)验证时抛出此异常 </li> <li> <code>Symfony\Component\Routing\Exception\MethodNotAllowedException</code> 请求路由时 HTTP Method 不正确 </li> <li> <code>Illuminate\Http\Exceptions\HttpResponseException</code> Laravel 的处理 HTTP 请求不成功时抛出此异常 </li> </ul> <h3 > 扩展 Laravel 的异常处理器 </h3> <p > 上面说了 Laravel 把 < code>\App\Exceptions\Handler</code> 注册成功了全局的异常处理器,代码中没有被 < code>catch</code > 到的异常,最后都会被 < code>\App\Exceptions\Handler</code > 捕获到,处理器先上报异常记录到日志文件里然后渲染异常响应再发送响应给客户端。但是自带的异常处理器的方法并不好用,很多时候我们想把异常上报到邮件或者是错误日志系统中,下面的例子是将异常上报到 Sentry 系统中,Sentry 是一个错误收集服务非常好用:</p>


public function report(Exception $exception)
{
    if (app()-&gt;bound(''sentry'') &amp;&amp; $this-&gt;shouldReport($exception)) {
        app(''sentry'')-&gt;captureException($exception);
    }

    parent::report($exception);
}

<p> 还有默认的渲染方法在表单验证时生成响应的 JSON 格式往往跟我们项目里统一的 < code>JOSN</code > 格式不一样这就需要我们自定义渲染方法的行为。</p>


public function render($request, Exception $exception)
{
    //如果客户端预期的是JSON响应,  在API请求未通过Validator验证抛出ValidationException后
    //这里来定制返回给客户端的响应.
    if ($exception instanceof ValidationException &amp;&amp; $request-&gt;expectsJson()) {
        return $this-&gt;error(422, $exception-&gt;errors());
    }

    if ($exception instanceof ModelNotFoundException &amp;&amp; $request-&gt;expectsJson()) {
        //捕获路由模型绑定在数据库中找不到模型后抛出的NotFoundHttpException
        return $this-&gt;error(424, ''resource not found.'');
    }


    if ($exception instanceof AuthorizationException) {
        //捕获不符合权限时抛出的 AuthorizationException
        return $this-&gt;error(403, "Permission does not exist.");
    }

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

<p> 自定义后,在请求未通过 < code>FormValidator</code > 验证时会抛出 < code>ValidationException</code>, 之后异常处理器捕获到异常后会把错误提示格式化为项目统一的 JSON 响应格式并输出给客户端。这样在我们的控制器中就完全省略了判断表单验证是否通过如果不通过再输出错误响应给客户端的逻辑了,将这部分逻辑交给了统一的异常处理器来执行能让控制器方法瘦身不少。</p> <h3 > 使用自定义异常 </h3> <p > 这部分内容其实不是针对 < code>Laravel</code > 框架自定义异常,在任何项目中都可以应用我这里说的自定义异常。</p> <p > 我见过很多人在 < code>Repository</code > 或者 < code>Service</code > 类的方法中会根据不同错误返回不同的数组,里面包含着响应的错误码和错误信息,这么做当然是可以满足开发需求的,但是并不能记录发生异常时的应用的运行时上下文,发生错误时没办法记录到上下文信息就非常不利于开发者进行问题定位。</p> <p > 下面的是一个自定义的异常类 </p>


namespace App\Exceptions\;

use RuntimeException;
use Throwable;

class UserManageException extends RuntimeException
{
    /**
     * The primitive arguments that triggered this exception
     *
     * @var array
     */
    public $primitives;
    /**
     * QueueManageException constructor.
     * @param array $primitives
     * @param string $message
     * @param int $code
     * @param Throwable|null $previous
     */
    public function __construct(array $primitives, $message = "", $code = 0, Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
        $this-&gt;primitives = $primitives;
    }

    /**
     * get the primitive arguments that triggered this exception
     */
    public function getPrimitives()
    {
        return $this-&gt;primitives;
    }
}

<p> 定义完异常类我们就能在代码逻辑中抛出异常实例了 </p>


class UserRepository
{
  
    public function updateUserFavorites(User $user, $favoriteData)
    {
        ......
        if (!$executionOne) {
            throw new UserManageException(func_get_args(), ''Update user favorites error'', ''501'');
        }
        
        ......
        if (!$executionTwo) {
            throw new UserManageException(func_get_args(), ''Another Error'', ''502'');
        }
        
        return true;
    }
}

class UserController extends ...
{
    public function updateFavorites(User $user, Request $request)
    {
        .......
        $favoriteData = $request-&gt;input(''favorites'');
        try {
            $this-&gt;userRepo-&gt;updateUserFavorites($user, $favoritesData);
        } catch (UserManageException $ex) {
            .......
        }
    }
}

<p> 除了上面 < code>Repository</code > 列出的情况更多的时候我们是在捕获到上面列举的通用异常后在 < code>catch</code > 代码块中抛出与业务相关的更细化的异常实例方便开发者定位问题,我们将上面的 < code>updateUserFavorites</code> 按照这种策略修改一下 </p>


public function updateUserFavorites(User $user, $favoriteData)
{
    try {
        // database execution
        
        // database execution
    } catch (QueryException $queryException) {
        throw new UserManageException(func_get_args(), ''Error Message'', ''501'' , $queryException);
    }

    return true;
}

<p> 在上面定义 < code>UserMangeException</code > 类的时候第四个参数 < code>$previous</code > 是一个实现了 < code>Throwable</code > 接口类实例,在这种情景下我们因为捕获到了 < code>QueryException</code > 的异常实例而抛出了 < code>UserManagerException</code > 的实例,然后通过这个参数将 < code>QueryException</code > 实例传递给 < code>PHP</code > 异常的堆栈,这提供给我们回溯整个异常的能力来获取更多上下文信息,而不是仅仅只是当前抛出的异常实例的上下文信息, 在错误收集系统可以使用类似下面的代码来获取所有异常的信息。</p>


while($e instanceof \Exception) {
    echo $e-&gt;getMessage();
    $e = $e-&gt;getPrevious();
}

<p> 异常处理是 < code>PHP</code > 非常重要但又容易让开发者忽略的功能,这篇文章简单解释了 < code>Laravel</code > 内部异常处理的机制以及扩展 < code>Laravel</code > 异常处理的方式方法。更多的篇幅着重分享了一些异常处理的编程实践,这些正是我希望每个读者都能看明白并实践下去的一些编程习惯,包括之前分享的 < code>Interface</code > 的应用也是一样。</p>

Laravel 中的异常处理

Laravel 中的异常处理

PS - 个人博客原文:Laravel 中的异常处理

这篇文章里,我们将研究 Laravel 框架中最重要也最少被讨论的功能 —— 异常处理。

Laravel 自带了一个异常处理类,它能够让你以简单、优雅的方式 report 和 render 异常。

文章的前半部分,我们将研究异常处理类(exception 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'' => env(''APP_DEBUG'', false),
...
...

正如注释中所说,debug 设置为 true 时,会显示详细的调试错误信息,设置为 false 时,只显示简单的错误信息。这个变量的默认值是通过 .env 文件中的 APP_DEBUG 环境变量设置的。

在开发环境下,你应该将其设置为 true,这样开发时更容易发现错误并修复它们。在生产环境下应该设置为 false,只显示简单的信息。

除了显示错误,你还可以将错误记录到日志文件中。打开配置文件 config/app.php,找到下面这行代码:

...
...
''log'' => env(''APP_LOG'', ''single''),

''log_level'' => env(''APP_LOG_LEVEL'', ''debug''),
...
...

日志文件默认路径为:storage/logs/laravel.log,大部分场景下它已经够用了。APP_LOG_LEVEL决定了错误日志被记录的级别。

上面就是关于异常和日志相关的配置的基本介绍。

然后,我们看一下 Laravel 自带的默认异常处理类。打开 app/Exceptions/Handler.php 文件:

<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that should not be reported.
     *
     * @var array
     */
    protected $dontReport = [
        \Illuminate\Auth\AuthenticationException::class,
        \Illuminate\Auth\Access\AuthorizationException::class,
        \Symfony\Component\HttpKernel\Exception\HttpException::class,
        \Illuminate\Database\Eloquent\ModelNotFoundException::class,
        \Illuminate\Session\TokenMismatchException::class,
        \Illuminate\Validation\ValidationException::class,
    ];

    /**
     * Report or log an exception.
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     *
     * @param  \Exception  $exception
     * @return void
     */
    public function report(Exception $exception)
    {
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {
        return parent::render($request, $exception);
    }
 
    /**
     * Convert an authentication exception into an unauthenticated response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Auth\AuthenticationException  $exception
     * @return \Illuminate\Http\Response
     */
    protected function unauthenticated($request, AuthenticationException $exception)
    {
        if ($request->expectsJson()) {
            return response()->json([''error'' => ''Unauthenticated.''], 401);
        }

        return redirect()->guest(route(''login''));
    }
}

这里有两个重要的方法:report()render()

report() 方法用于将异常报告给外部服务或者记录日志,默认情况下,将异常传递给记录异常的基类。注意对于添加到 dontReport 属性数组中的异常类,则不会被报告(执行此方法)。

render() 方法负责将给定的异常转换为将被发送回浏览器的 HTTP 响应。默认情况下,异常将传递给为你生成响应的基类。你可以在这里针对不同的异常,自定义相应的响应。

最后,提供了一个 unauthenticated() 方法用于处理 AuthenticationException 异常,能够让你决定未认证时用户能够看到的信息。

自定义异常

下面我们创建一个自定义异常类来处理 CustomException 类型的异常。这个自定义异常类背后的原则同时支持异常管理和自定义异常信息的输出。

我们创建一个异常类文件app/Exceptions/CustomException.php

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

注意到,CustomException 扩展自 Exception 类。出于演示目的,我们只讨论 render() 方法,当然你也可以自定义 report() 方法。

上面的 render() 中,我们将显示 errors.custom 页面给用户。通过这种方式,你可以为自己的异常自定义页面输出。

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

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

要想上面的代码执行,我们还需要修改 app/Exceptions/Handler.php 中的 render() 方法:

...
...
/**
 * Render an exception into an HTTP response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Exception  $exception
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $exception)
{
    if ($exception instanceof \App\Exceptions\CustomException)  {
        return $exception->render($request);
    }

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

在这里,我们先检查异常类型,然后调用我们自己的异常的 render() 方法从而实现输出。

所有到此所有的就介绍完了。

让我们继续,创建一个控制器 app/Http/Controllers/ExceptionController.php 来测试我们写的自定义异常类:

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

然后在 routes/web.php 中添加一个路由:

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

然后访问 http://your-laravel-site.com/exception/index , 最终应当输出 errors.custom 视图。

所以以上就是 Laravel 处理异常的过程,希望你会喜欢。

本文翻译自,部分有所改动。 原文链接:<a href="https://code.tutsplus.com/tutorials/exception-handling-in-laravel--cms-30210" target="_blank">https://code.tutsplus.com/tutorials/exception-handling-in-laravel--cms-30210</a>

laravel 异常处理

laravel 异常处理

1. 场景:正常 PC 访问时,如点击商品时,此时商品刚好下架,那么要如何给出相应的提示?

php artisan make:exception InvalidRequestException

从 5.5 版本后,支持在异常类中定义 render () 方法。异常触发时会自动调用 render();

namespace App\Exceptions;

use Exception;
use Illuminate\Http\Request;

class InvalidRequestException extends Exception
{
    public function __construct(string $message = "", int $code = 400)
    {
        parent::__construct($message, $code);
    }

    public function render(Request $request)
    {
        if ($request->expectsJson()) {
            // json() 方法第二个参数就是 Http 返回码
            return response()->json([''msg'' => $this->message], $this->code);
        }

        return view(''pages.error'', [''msg'' => $this->message]);
    }
}

2. 再在 view/pages/error.blade.php 里做好模板

@extends(''layouts.app'')
@section(''title'', ''错误'')

@section(''content'')
<div class="card">
    <div class="card-header">错误</div>
    <div class="card-body text-center">
        <h1>{{ $msg }}</h1>
        <a class="btn btn-primary" href="{{ route(''root'') }}">返回首页</a>
    </div>
</div>
@endsection

3. 当出现异常时,laravel 默认会写到日志里,那么如何关掉因这个类而产生的日志呢

app/Exceptions/Handler.php

    protected $dontReport = [
        InvalidRequestException::class,
    ];

4. 还有一种异常,即要给个信息到用户看,也在存个信息到日志,而这俩错误信息是不同的内容时:

  php artisan make:exception InternalException

namespace App\Exceptions;

use Exception;
use Illuminate\Http\Request;

class InternalException extends Exception
{
    protected $msgForUser;

    public function __construct(string $message, string $msgForUser = ''系统内部错误'', int $code = 500)
    {
        parent::__construct($message, $code); //这里会自动存错误到日志
        $this->msgForUser = $msgForUser;
    }

    public function render(Request $request)
    {
        if ($request->expectsJson()) {  //这里给上面指定的错误信息给到用户
            return response()->json([''msg'' => $this->msgForUser], $this->code);
        }

        return view(''pages.error'', [''msg'' => $this->msgForUser]);
    }
}

 在控制器中就直接这么用

if (!$product->on_sale) {
            throw new InvalidRequestException(''商品未上架'');
        }

 

Laravel 源码核心解读:全局异常处理

Laravel 源码核心解读:全局异常处理

Laravel 为用户提供了一个基础的全局异常拦截处理器App\Exceptions\Hander。如果没有全局的异常错误拦截器,那我们在每个可能发生错误异常的业务逻辑分支中,都要使用 try ... catch,然后将执行结果返回 Controller层,再由其根据结果来构造相应的 Response,那代码冗余的会相当可以。

全局异常错误处理,是每个框架都应该具备的,这次我们就通过简析 Laravel 的源码和执行流程,来看一下此模式是如何被运用的。

源码解析

laravel/laravel 脚手架中有一个预定义好的异常处理器:

app/Exceptions/Handler.php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    // 不被处理的异常错误
    protected $dontReport = [];

    // 认证异常时不被flashed的数据
    protected $dontFlash = [
        ''password'',
        ''password_confirmation'',
    ];
    // 上报异常至错误driver,如日志文件(storage/logs/laravel.log),第三方日志存储分析平台
    public function report(Exception $exception)
    {
        parent::report($exception);
    }
    // 将异常信息响应给客户端
    public function render($request, Exception $exception)
    {
        return parent::render($request, $exception);
    }
}

当 Laravel 处理一次请求时,在启动文件中注册了以下服务:
bootstrap/app.php

// 绑定 http 服务提供者
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
// 绑定 cli 服务提供者
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);
// 这里将异常处理器的服务提供者绑定到了 `App\Exceptions\Handler::class`
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

而后进入请求捕获,处理阶段:
public/index.php

// 使用 http 服务处理请求
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
// http 服务处理捕获的请求 $requeset
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
Illuminate\Contracts\Http\Kernel::class 具体提供者是 App\Http\Kernel::class 继承至 Illuminate\Foundation\Http\Kernel::class,我们去其中看 http 服务 的 handle 方法是如何处理请求的。

请求处理阶段:
Illuminate\Foundation\Http\Kernel::classhandle 方法对请求做一次处理,如果没有异常则分发路由,如果有异常则调用 reportExceptionrenderException 方法记录&渲染异常。

具体处理者则是我们在 bootstrap/app.php 中注册绑定的异常处理服务 Illuminate\Contracts\Debug\ExceptionHandler::classreport & render,具体的服务即绑定的 App\Exceptions\Handler::class

public function handle($request)
{
    try {
        // 没有异常 则进入路由分发
        $request->enableHttpMethodParameterOverride();
        $response = $this->sendRequestThroughRouter($request);
    } catch (Exception $e) {
        // 捕获异常 则 report & render
        $this->reportException($e);
        $response = $this->renderException($request, $e);
    } catch (Throwable $e) {
        $this->reportException($e = new FatalThrowableError($e));
        $response = $this->renderException($request, $e);
    }

    $this->app[''events'']->dispatch(
        new Events\RequestHandled($request, $response)
    );

    return $response;
}

//Report the exception to the exception handler.
protected function reportException(Exception $e)
{
    // 服务`Illuminate\Contracts\Debug\ExceptionHandler::class` 的 report 方法
    $this->app[ExceptionHandler::class]->report($e);
}
//Render the exception to a response.
protected function renderException($request, Exception $e)
{
    // 服务`Illuminate\Contracts\Debug\ExceptionHandler::class` 的 render 方法
    return $this->app[ExceptionHandler::class]->render($request, $e);
}

handler 方法作为请求处理的入口,后续的路由分发,用户业务调用(controller, model)等执行的上下文依然在此方法中,故异常也能在这一层被捕获。

然后我们就可以在业务中通过 throw new CustomException($code, "错误异常描述"); 的方式将控制流程转交给全局异常处理器,由其解析异常并构建响应实体给客户端,这一模式在 Api服务 的开发中是效率极高的。

laravel 的依赖中有 symfony 这个超级棒的组件库,symfony 为我们提供了详细的 Http 异常库,我们可以直接借用这些异常类(当然也可以自定义)

clipboard.png

laravel 有提供 abort 助手函数来实现创建一个异常错误,但主要面向 web 网站(因为laravel主要就是用来开发后台的嘛)的,对 Api
不太友好,而且看源码发现只顾及了 404 这货。

/**
 * abort(401, "你需要登录")
 * abort(403, "你登录了也白搭")
 * abort(404, "页面找不到了")
 * Throw an HttpException with the given data.
 *
 * @param  int     $code
 * @param  string  $message
 * @param  array   $headers
 * @return void
 *
 * @throws \Symfony\Component\HttpKernel\Exception\HttpException
 */
public function abort($code, $message = '''', array $headers = [])
{
    if ($code == 404) {
        throw new NotFoundHttpException($message);
    }

    throw new HttpException($code, $message, null, $headers);
}

即只有 404 用了具体的异常类去抛出,其他的状态码都一股脑的归为 HttpException,这样就不太方便我们在全局异常处理器的 render 中根据 Exception 的具体类型来分而治之了,但 abort 也的确是为了方便你调用具体的错误页面的 resources/views/errors/{statusCode.blade.php} 的,需要对 Api 友好自己改写吧。

使用场景

// 业务代码 不满足直接抛出异常即可
if ("" = trim($username)) {
    throw new BadRequestHttpException("用户名必须");
}
// 全局处理器
public function render($request, Exception $exception)
{
    if ($exception instanceof BadRequestHttpException) {
        return response()->json([
            "err" => 400,
            "msg" => $exception->getMessage()
        ]);
    }
    
    if ($exception instanceof AccessDeniedHttpException) {
        return response()->json([
            "err" => 403,
            "msg" => "unauthorized"
        ]);
    }
    
    if ($exception instanceof NotFoundHttpException) {
        return response()->json([
            "err" => 403,
            "msg" => "forbidden"
        ]);
    }
    
    if ($exception instanceof NotFoundHttpException) {
        return response()->json([
            "err" => 404,
            "msg" => "not found"
        ]);
    }
    
    if ($exception instanceof MethodNotAllowedHttpException) {
        return response()->json([
            "err" => 405,
            "msg" => "method not allowed"
        ]);
    }
    
    if ($exception instanceof MethodNotAllowedHttpException) {
        return response()->json([
            "err" => 406,
            "msg" => "你想要的数据类型我特么给不了啊"
        ]);
    }
    
    if ($exception instanceof TooManyRequestsHttpException) {
        return response()->json([
            "err" => 429,
            "msg" => "to many request"
        ]);
    }
    
    return parent::render($request, $exception);
}

laravel 请求异常处理

laravel 请求异常处理

laravel 是一种流行的 php 框架,它提供了一个强大而灵活的系统来构建 web 应用程序。但是,在开发过程中,难免会遇到请求异常的情况。在本文中,我们将讨论 laravel 请求异常的处理方法。

  1. 异常的分类

Laravel 中请求异常可以分为两种类型:程序异常和 HTTP 异常。

程序异常是在代码运行过程中出现的异常,例如 PHP 抛出的致命错误,未捕获的异常等等。

HTTP 异常是指在 HTTP 请求中出现的异常,例如 404 Not Found,500 Internal Server Error 等等。

不同类型的异常需要采用不同的处理方式。

  1. 程序异常的处理

程序异常可能会出现在 Laravel 控制器中,如果不加处理,将会弹出一个页面显示错误消息。这并不是用户期望看到的,因此需要对程序异常进行处理。

Laravel 给我们提供了两种方法处理程序异常。第一种是使用异常处理器,第二种是使用全局异常处理。

2.1 异常处理器

Laravel 异常处理器是一个类,处理应用程序抛出的异常。如果我们想要抛出异常时控制器返回一个 JSON 格式的响应,我们可以创建一个自定义异常处理器。下面是一个例子:

<?php

namespace AppExceptions;

use Exception;
use IlluminateFoundationExceptionsHandler as ExceptionHandler;

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

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

    /**
     * Render an exception into an HTTP response.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Exception  $exception
     * @return IlluminateHttpResponse
     */
    public function render($request, Exception $exception)
    {
        if ($exception instanceof IlluminateDatabaseEloquentModelNotFoundException) {
            return response()->json([
                ''error'' => ''Resource not found''
            ], 404);
        }

        return parent::render($request, $exception);
    }
}
登录后复制

在这个例子中,我们继承了 Laravel 的异常处理器类,并重写了 render 方法。在 render 方法中,我们检查了异常类型是否是 IlluminateDatabaseEloquentModelNotFoundException。如果是,我们返回一个 JSON 格式的响应。

我们还可以在这个方法中处理其他的程序异常。这种处理方式的好处是,我们可以为每种类型的异常编写自定义的处理器。这样我们就能够预测到我们会得到什么样的响应。

2.2 全局异常处理

使用全局异常处理,我们可以捕获应用程序中的所有异常,而不是为每种异常编写单独的处理器。下面是一个例子:

<?php

namespace AppExceptions;

use Exception;
use IlluminateFoundationExceptionsHandler as ExceptionHandler;

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

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

    /**
     * Render an exception into an HTTP response.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Exception  $exception
     * @return IlluminateHttpResponse
     */
    public function render($request, Exception $exception)
    {
        if ($exception instanceof SymfonyComponentHttpKernelExceptionHttpException) {
            $code = $exception->getStatusCode();

            return response()->json([
                ''error'' => ''HTTP Exception'',
                ''status'' => $code
            ], $code);
        }

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

    /**
     * Render the given HttpException.
     *
     * @param  SymfonyComponentHttpKernelExceptionHttpException  $e
     * @return IlluminateHttpResponse
     */
    protected function renderHttpException(HttpException $e)
    {
        $status = $e->getStatusCode();

        if (view()->exists("errors.{$status}")) {
            return response()->view("errors.{$status}", [''exception'' => $e], $status, $e->getHeaders());
        } else {
            return $this->convertExceptionToResponse($e);
        }
    }
}
登录后复制

在这个例子中,我们重写了 render 方法,检查异常类型是否是 SymfonyComponentHttpKernelExceptionHttpException。如果是,我们创建了一个 JSON 格式的响应,包括错误消息和 HTTP 状态码。

如果我们需要呈现 HTML 页面,我们还可以重写 renderHttpException 方法,以渲染自定义的异常页面。

  1. HTTP 异常的处理

Laravel 提供了一种简单的方法处理 HTTP 异常。通过自定义 app/Exceptions/Handler.php 中的 render 方法,我们可以返回指定的 HTTP 状态码。以下是一个例子:

public function render($request, Exception $exception)
{
    if ($this->isHttpException($exception)) {
        return $this->renderHttpException($exception);
    } else {
        return parent::render($request, $exception);
    }
}

protected function renderHttpException(HttpException $exception)
{
    return response()->view(''errors.'' . $exception->getStatusCode(), [], $exception->getStatusCode());
}
登录后复制

在上面的例子中,我们检查异常是否是 HTTP 异常。如果是,我们使用 getStatusCode 方法获取 HTTP 状态码,并将其用作视图名称。在这个例子中,我们只是返回了一个对应状态码的视图。

  1. 结论

在本文中,我们介绍了 Laravel 中程序和 HTTP 异常的处理方法。我们学习了如何使用异常处理器和全局异常处理来处理程序异常,以及如何自定义 render 方法来处理 HTTP 异常。对于 Laravel 开发人员来说,正确处理异常是非常重要的。通过使用这些技术,我们能够更加精确地控制应用程序的行为,提高其可靠性和稳定性。

以上就是laravel 请求异常处理的详细内容,更多请关注php中文网其它相关文章!

今天的关于Laravel 核心解读 -- 异常处理laravel哪个类处理异常的分享已经结束,谢谢您的关注,如果想了解更多关于Laravel 中的异常处理、laravel 异常处理、Laravel 源码核心解读:全局异常处理、laravel 请求异常处理的相关知识,请在本站进行查询。

本文标签: