在app目录下创建Exception
文件夹,在该文件夹中创建一个名为ExceptionHandle
的类,继承于think\exception
下的Handle
父类
namespace app\Exception;
use think\exception\Handle;
class ExceptionHandle extends Handle
{
}
在TP6中,你可能已经发现在app文件夹下已经有一个ExceptionHandle
类了,这个类重写了父类Handle
中的两个方法
/**
* 记录异常信息(包括日志或者其它方式记录)
*
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception): void
{
// 使用内置的方式记录异常日志
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @access public
* @param \think\Request $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
// 添加自定义异常处理机制
// 其他错误交给系统处理
return parent::render($request, $e);
}
下面的步骤就是对这两个方法就行重写。
render
方法render
方法是TP6中的异常处理方法,它将TP6中的异常反馈给HTTP请求。
通常将异常分为用户端异常和服务端异常,用户端异常就是用户在请求数据时产生的一些异常,如Token过期、密码错误等;服务端异常可能是服务器、数据库等和用户无关的异常。在对TP6的异常进行自定义时,主要区分异常是来自用户端还是来自服务端。
为了区分两种不同的异常,在Exception
目录下面创建一个BaseException
子类,继承于think\
下的Exception
父类。在BaseException
中创建三个成员变量,并赋初值。
namespace app\exception;
use think\Exception;
class BaseException extends Exception
{
public $code = 400;
public $msg = 'invalid parameters';
public $errorCode = 999;
public function __construct($params=[])
{
if(!is_array($params)){
return;
}
if(array_key_exists('code',$params)){
$this->code = $params['code'];
}
if(array_key_exists('msg',$params)){
$this->msg = $params['msg'];
}
if(array_key_exists('errorCode',$params)){
$this->errorCode = $params['errorCode'];
}
}
}
think\Exception
类继承了\Exception
,Exception
是所有异常的基类。BaseException
中的变量$code
表示HTTP状态码,$msg
表示返回信息,$errorCode
表示错误码。请注意,HTTP状态码和错误码并不是等价的,HTTP状态码是是用以表示网页服务器超文本传输协议响应状态的3位数字代码,它是国际通用的,而错误码是我们自定义的一套数字代码,没有比较成熟或者通用的设计方法,可以用于对应错误信息、判断是否返回了正确的信息等。
下面所有继承于BaseException
的类并且带有都是自定义异常,比如说我定义了一个参数异常的类ParameterException
:
namespace app\exception;
/**
* Class ParameterException
* 通用参数类异常错误
*/
class ParameterException extends BaseException
{
public $code = 400;
public $errorCode = 10000;
public $msg = "invalid parameters";
}
回到ExceptionHandle
类中,下面对render
方法进行重写
/**
* @param Request $request
* @param Throwable $e
* @return Response
*/
public function render($request, Throwable $e): Response
{
if ($e instanceof BaseException) {
$code = $e->code;
$msg = $e->msg;
$errorCode = $e->errorCode;
} else {
if (env('APP_DEBUG') == '1') {
return parent::render($request, $e);
}
$code = 500;
$msg = '服务器异常';
$errorCode = 999;
}
$data = {
'msg' => $msg,
'errorCode' => $errorCode,
'url' => $request->url()
};
return json(null, $code);
}
前面说到render
方法将TP6中的异常反馈给HTTP请求,$e
是异常的一个实例,通过类型运算符instanceof
判断该异常是否继承于BaseException
。在开发环境下,通常需要详细的TP6异常信息,所以还需判断APP_DEBUG
是否为TRUE
。
report
方法report
方法是TP6记录日志的一个方法,在异常发生时记录异常信息,对于用户异常和服务端异常,通常需要记录的是服务端异常,所以对report
方法重写如下:
public function report(Throwable $exception): void
{
//系统异常才记录日志
if (!($exception instanceof BaseException))
parent::report($exception);
}
app目录下有一个provider.php文件,它是容器Provider的定义文件。需要在app目录下面的provider.php文件中绑定异常处理类,即上面实现的ExceptionHandle
类。
use app\exception\ExceptionHandle;
// 容器Provider定义文件
return [
'think\exception\Handle' => ExceptionHandle::class
];
本文是对TP6异常处理的一些见解,其实在不同的框架或者编程语言中都能按照这种思路来编写自定义异常处理。
语言和框架只是一个工具,重要的是工具背后的思想。