重写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异常处理类的更多相关文章
- 扩展PHP内置的异常处理类
在try代码块中,需要使用throw语句抛出一个异常对象,才能跳到转到catch代码块中执行,并在catch代码块中捕获并使用这个异常类的对象.虽然在PHP中提供的内置异常处理类Exception,已 ...
- Laravel异常处理
Laravel异常处理 标签(空格分隔): php 自定义异常类 <?php namespace App\Exceptions; use Throwable; use Exception; cl ...
- php 异常处理类
PHP具有很多异常处理类,其中Exception是所有异常处理的基类. Exception具有几个基本属性与方法,其中包括了: message 异常消息内容code 异常代码file 抛出异常的文件名 ...
- 【转载】 C++多继承中重写不同基类中相同原型的虚函数
本篇随笔为转载,原文地址:C++多继承中重写不同基类中相同原型的虚函数. 在C++多继承体系当中,在派生类中可以重写不同基类中的虚函数.下面就是一个例子: class CBaseA { public: ...
- 面向对象编程(九)——面向对象三大特性之继承以及重写、Object类的介绍
面向对象三大特性 面向对象三大特征:继承 :封装/隐藏 :多态(为了适应需求的多种变化,使代码变得更加通用!) 封装:主要实现了隐藏细节,对用户提供访问接口,无需关心方法的具体实现. 继承:很好的实现 ...
- Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志
在使用SimpleMappingExceptionResolver实现统一异常处理后(参考Spring MVC的异常统一处理方法), 发现出现异常时,log4j无法在控制台输出错误日志.因此需要自定义 ...
- or1200处理器的异常处理类指令介绍
下面内容摘自<步步惊芯--软核处理器内部设计分析>一书 我们在计算机体系结构的学习中知道:中断实质上包含由外部事件引起的硬中断(又称外中断)和由内部预先安排的特定指令或内部异常引起的软中断 ...
- springboot统一异常处理类及注解参数为数组的写法
统一异常处理类 package com.wdcloud.categoryserver.common.exception; import com.wdcloud.categoryserver.commo ...
- php异常处理类
<?php header('content-type:text/html;charset=UTF-8'); // 创建email异常处理类 class emailException extend ...
随机推荐
- 重闯Sqli-labs关卡第一天(1-4关)
前言 之前暑假闯了很多关但是最近刷BUGku的题 遇到SQL注入题就凉... 垃圾的我只能继续硬着头皮重新再来学习,再来闯. 第一关:字符型注入 字符型注入就是注入点的数据类型是字符型.字符型注入与数 ...
- 掌握这些springboot的配置方式,让你工作效率翻个倍!
springboot的多种配置方式 java配置主要靠java类和一些注解,比较常用的注解有: @Configuration :声明一个类作为配置类,代替xml文件 @Bean :声明在方法上,将方法 ...
- 追踪聚光特效怎么实现,有Vegas就够了
舞台聚光灯大家一定都不陌生,在电视上某些颁奖活动里,主持人的进场一定伴随着舞台灯光的聚光效果.随着主持人的移动,灯光也随之移动.这里的舞台灯光就起到了一个追踪聚光的效果. Vegas Pro 16 增 ...
- python - os.sep用法
python是跨平台的.在Windows上,文件的路径分隔符是'\',在Linux上是'/'.为了让代码在不同的平台上都能运行,那么路径应该写'\'还是'/'呢?使用os.sep的话,就不用考虑这个了 ...
- Docker一些基本操作
1.停止所有的container,这样才能够删除其中的images: docker stop $(docker ps -a -q) 如果想要删除所有container的话再加一个指令: docker ...
- Spring Boot 2.x 多数据源配置之 JPA 篇
场景假设:现有电商业务,商品和库存分别放在不同的库 配置数据库连接 app: datasource: first: driver-class-name: com.mysql.cj.jdbc.Drive ...
- MySQL错误日志(Error Log)
错误日志是一个文本文件,记录了 MySQL Server 每次启动和关闭的详细信息以及运行过程中所有较为严重的警告和错误信息.在遇到问题时,应该首先查看这个文件. 如何开启 使用命令 SHOW VAR ...
- 第7.15节 Python中classmethod定义的类方法详解
第7.15节 Python中classmethod定义的类方法详解 类中的方法,除了实例方法外,还有两种方法,分别是类方法和静态方法.本节介绍类方法的定义和使用. 一. 类方法的定义 在类中定 ...
- PyQt(Python+Qt)学习随笔:QDateTimeEdit日期时间编辑部件
专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 Designer输入部件中,Date/Time E ...
- 关于Python中中文文本文件使用二进制方式读取后的解码UnicodeDecodeError问题
最近老猿在进行文件操作的验证测试,发现对于中文文本文件如果使用二进制方式打开,返回的类型是bytes,如果要转换成可读的字符串信息需要进行解码.可是老猿使用decode()或decode(" ...