• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    如何重写Laravel异常处理类详解

    现在开发前后端分离变得越来越流行了,后端只提供接口返回json格式的数据,即使是错误信息也要以json格式来返回,然而目前无论是Laravel框架还是ThinkPHP框架,都只提供了返回json数据的方法,对异常的处理并不是以json格式来返回给我们,所以这里就需要我们自己来改写。

    首先我们在app/Exceptions目录新建一个ExceptionHandler.php继承自Handler.php

    namespace App\Exceptions;
    
    
    class ExceptionHandler extends Handler
    {
    
    }

    然后我们在bootstrap/app.php中,使用我们自定义的异常处理类ExceptionHandler替换掉默认的Handler类

    //改为我们自定义的ExceptionHandler类
    $app->singleton(
     Illuminate\Contracts\Debug\ExceptionHandler::class,
     App\Exceptions\ExceptionHandler::class
    );

    接下来我们就开始重写渲染方法

    在render方法里,我们根据.env文件中的APP_DEBUG来判断,如果是调试模式,我们还是按照默认方式来渲染错误,如果是非调试模式,我们就返回JSON格式的信息

    namespace App\Exceptions;
    
    use Exception;
    
    class ExceptionHandler extends Handler
    {
     public function render($request, Exception $exception)
     {
     if (env('APP_DEBUG')) {
      return parent::render($request, $exception);
     }
     return response()->json([
      'code' => $exception->getCode(),
      'msg' => $exception->getMessage()
     ]);
     }
    }

    这样我们就可以根据APP_DEBUG的值设置是否返回JSON格式的数据了,现在我们把.env的APP_DEBUG的值设为false来测试一下,然后我们故意把代码写错,通过postman或浏览器来访问接口

    Route::get('/', function () {
     //这是一段缺少了分号的代码,会报异常
     echo 'Hello World!'
    });

    在APP_DEBUG=true的情况下还仍然是默认渲染,方便我们查找错误排错

    异常类默认会把异常以日志的形式记录在storage/logs目录下,并且以laravel-日期(YYYY-MM-DD)命名的形式,.log为后缀保存错误日志

    我们打开这个日志文件查看记录的错误信息,我们可以发现错误信息记录的非常详细,除了错误说明之外,还记录了调用栈,如下图所示

    基本上红框里的信息就够我们排错了,不需要像现在这样记录的这么详细,所以要想不记录调用栈,我们可以重写report方法

    首先我们看一下框架的report方法,代码在(src/Illuminate/Foundation/Exceptions/Handler.php),我用红框框起来的代码就是调用栈信息,我们在重写这个方法时只需要完全拷贝这个方法里的所有代码到我们自定义的report方法里,然后把红框里的代码去掉即可

    我们在我们自定义的异常处理类ExceptionHandler.php中重写report方法

    public function report(Exception $exception)
    {
     if ($this->shouldntReport($exception)) {
     return;
     }
    
     if (Reflector::isCallable($reportCallable = [$exception, 'report'])) {
     return $this->container->call($reportCallable);
     }
    
     try {
     $logger = $this->container->make(LoggerInterface::class);
     } catch (Exception $ex) {
     throw $exception;
     }
    
     $logger->error(
     $exception->getMessage()
     );
    }

    然后我们再重新请求一下接口再去查看错误日志的记录,可以发现确实没有记录调用栈信息了,但是下面的信息还是不够,我们没法根据下面的信息判断错误发生在哪一个文件和哪一行,如果能在记录错误信息的时候同时记录发生错误的文件和行就更好了,所以借着修改report方法

    public function report(Exception $exception)
    {
     if ($this->shouldntReport($exception)) {
     return;
     }
    
     if (Reflector::isCallable($reportCallable = [$exception, 'report'])) {
     return $this->container->call($reportCallable);
     }
    
     try {
     $logger = $this->container->make(LoggerInterface::class);
     } catch (Exception $ex) {
     throw $exception;
     }
    
     $logger->error(
     $exception->getMessage()." at ".$exception->getFile().":".$exception->getLine()
     );
    }

    在代码里我通过exception的getFile()、getLine()方法加上了文件和行数,保存代码再次访问接口,查看错误日志文件我们可以看到发生错误的文件和行数已经记录下来了,有了这些信息基本我们就可以找到错误

    截止到这里实现最初的需求我们的ExceptionHandler.php只需要有这些代码

    namespace App\Exceptions;
    
    
    use Exception;
    use Illuminate\Support\Reflector;
    use Psr\Log\LoggerInterface;
    
    class ExceptionHandler extends Handler
    {
    
     public function render($request, Exception $exception)
     {
     if (env('APP_DEBUG')) {
      return parent::render($request, $exception);
     }
     return response()->json([
      'code' => $exception->getCode(),
      'msg' => $exception->getMessage()
     ]);
     }
    
     public function report(Exception $exception)
     {
     if ($this->shouldntReport($exception)) {
      return;
     }
    
     if (Reflector::isCallable($reportCallable = [$exception, 'report'])) {
      return $this->container->call($reportCallable);
     }
    
     try {
      $logger = $this->container->make(LoggerInterface::class);
     } catch (Exception $ex) {
      throw $exception;
     }
    
     $logger->error(
      $exception->getMessage()." at ".$exception->getFile().":".$exception->getLine()
     );
     }
    }

    然后还不够,我们发现刚刚我们把服务器端的错误信息以JSON格式返回给客户端了,这是不允许的,我们应该只把一些客户端错误返回给客户端,比如密码不足六位、身份证不合法诸如此类,而服务端出现错误时我们只返回给客户端一个模糊的信息即可,比如“服务器错误”,把真实的服务器错误信息记录在日志里面方便开发人员排查错误

    所以我们需要定义一个客户端异常专门用户返回客户端错误,使用如下命令在app/Exceptions目录下生成一个ClientException.php文件

    php artisan make:exception ClientException

    修改为构造方法为如下代码

    namespace App\Exceptions;
    
    use Exception;
    
    class ClientException extends Exception
    {
     public function __construct($code, $msg)
     {
     parent::__construct($msg, $code);
     }
    }

    接着我们继续修改ExceptionHandler.php

    namespace App\Exceptions;
    
    
    use Exception;
    use Illuminate\Support\Reflector;
    use Psr\Log\LoggerInterface;
    
    class ExceptionHandler extends Handler
    {
     /**
     * @var int 错误码
     */
     protected $code;
     /**
     * @var string 错误信息
     */
     protected $message;
    
     protected $dontReport = [
     ClientException::class
     ];
    
     public function render($request, Exception $exception)
     {
     if ($exception instanceof ClientException) {
      $this->code = $exception->getCode();
      $this->message = $exception->getMessage();
     } else {
      if (env('APP_DEBUG')) {
      return parent::render($request, $exception);
      }
      
      $this->code = 500;
      $this->message = '服务器错误';
     }
     
     return response()->json([
      'code' => $this->code,
      'msg' => $this->message
     ]);
     }
    
     public function report(Exception $exception)
     {
     if ($this->shouldntReport($exception)) {
      return;
     }
    
     if (Reflector::isCallable($reportCallable = [$exception, 'report'])) {
      return $this->container->call($reportCallable);
     }
    
     try {
      $logger = $this->container->make(LoggerInterface::class);
     } catch (Exception $ex) {
      throw $exception;
     }
    
     $logger->error(
      $exception->getMessage()." at ".$exception->getFile().":".$exception->getLine()
     );
     }
    }

    对于上面的修改做一下说明,laravel的$dontReport属性的异常类都不会被上报,因为客户端错误信息我们不需要记录,所以将其添加到$dontReport属性里,并且在render方法里把异常大概分为了两大类,一大类就是客户端异常,另一大类就是服务器异常,我们把服务器异常统一code为500,错误信息为服务器错误,将真实的错误信息记录在了错误日志里,避免把服务器信息暴露给了客户端。

    现在我们来测试我们重写异常的结果

    假如我们想返回客户端异常,比如没有权限,这类客户端异常在错误日志里都不会产生记录,我们本身也不需要记录

    Route::get('/', function () {
     throw new \App\Exceptions\ClientException(403, '你没有权限');
    });

    对于服务器端的错误,如少些了分号,客户端就只会知道服务器的某个接口出了问题,但是不清楚具体问题是什么

    Route::get('/', function () {
     echo 'Hello World!'
    });

    但是真实的错误信息会记录在错误日志里,我们仍旧可以通过错误日志来修改我们服务端的错误

    我们还可以在render方法中加入告警代码,如果是服务端错误就给管理员发送邮件。

    至此,我们的重写Laravel异常处理类就算完成啦,希望对正在准备使用Laravel做前后端分离项目的你有所帮助。

    到此这篇关于如何重写Laravel异常处理类的文章就介绍到这了,更多相关重写Laravel异常处理类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • Laravel如何实现适合Api的异常处理响应格式
    • Laravel 修改验证异常的响应格式实例代码详解
    • laravel框架使用FormRequest进行表单验证,验证异常返回JSON操作示例
    • Laravel 解决composer相关操作提示php相关异常的问题
    • Laravel 5.5 异常处理 & 错误日志的解决
    • 解决laravel 表单提交-POST 异常的问题
    • laravel框架 api自定义全局异常处理方法
    • Laravel核心解读之异常处理的实践过程
    • 浅谈Laravel中使用Slack进行异常通知
    上一篇:ThinkPHP6.0如何利用自定义验证规则规范的实现登陆
    下一篇:PHP设计模式之命令模式示例详解
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    如何重写Laravel异常处理类详解 如何,重写,Laravel,异常,处理,