一般项目中路由分为返回模板引擎页面
和返回api接口json数据
,两种方式异常需要返回不同的内容,如果是模板引擎页面遇到异常需要返回错误页面,如果是api接口遇到异常需要返回json数据。
开发模式和上线模式应该返回不同的内容,开发模式应该尽可能返回具体的错误信息,上线模式则不能返回具体的错误信息,一般显示“服务器错误,请稍后重试”类似友好的提示,而不是显示一堆报错代码(既不友好又不安全)。
如果有更好的方法,欢迎提出意见。
在访问路由时,若控制器不对,需要使用空控制器
拦截报错。
方法找不到可以修改app目录下的BaseController控制器重写__call方法。
自定义拦截报错信息,可以使用Provider自定义错误处理类,重写render方法,根据不同的错误返回不同的数据。
同第3点。
(1)
.env
文件定义APP_DEBUG
区分开发模式和线上模式,true表示开发模式,false表示线上模式:
APP_DEBUG = true
(2) 在
app/common.php
文件定义api
返回的数据格式:
function show($status, $message = 'error', $data = [], $httpStatus = 200){
$result = [
"status" => $status,
"message" => $message,
"result" => $data
];
return json($result, $httpStatus);
}
(3) 在当前应用下新建一个provider.php,并指定自定义异常处理类:
// 容器Provider定义文件
return [
'think\exception\Handle' => 'app\\admin\\exception\\Http',
];
(4) 定义状态码配置,可以在
config
文件夹下新建status.php
添加相应的状态码配置:
return [
"success" => 1,
"error" => 0,
"http_status" => [
"not_found" => 404,
"validate_error" => 422,
"internal_error" => 500
]
];
(5) 准备错误页面(404,500等)
在app/controller
目录新建Error
类(文件名固定为Error
):
这里需要注意的是,多应用的控制器应该定义在应用文件夹里,这里定义在app目录是为了作用于app下全部应用。
namespace app\controller;
class Error
{
public function __call($name, $arguments)
{
if(request()->isAjax()){
return show(config("status.error"), env('app_debug') ? "控制器{
$name}找不到" : '当前请求资源不存在,请稍后再试', [], config("status.http_status.not_found"));
}else{
return view(root_path() . 'public/error/admin/404.html', ['e' => env('app_debug') ? "控制器{
$name}找不到" : '当前请求资源不存在,请稍后再试'], config("status.http_status.not_found"));
}
}
}
在app/BaseController.php
控制器添加 __call
方法:
public function __call($name, $arguments)
{
if(request()->isAjax()){
return show(config("status.error"), env('app_debug') ? "找不到{
$name}方法" : '当前请求资源不存在,请稍后再试', [], config("status.http_status.not_found"));
}else{
return view(root_path() . 'public/error/admin/404.html', ['e' => env('app_debug') ? "{
$name}方法找不到" : '当前请求资源不存在,请稍后再试'], config("status.http_status.not_found"));
}
}
app\\admin\\exception\\Http
:
namespace app\admin\exception;
use ErrorException;
use Exception;
use InvalidArgumentException;
use ParseError;
use PDOException;
use think\exception\ClassNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\RouteNotFoundException;
use think\Response;
use Throwable;
use TypeError;
class Http extends Handle
{
/**
* 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
{
$returnCode = config("status.error");
$returnMessage = "系统异常,请稍后再试";
$returnData = [];
$httpStatus = 500;
if($e instanceof BusinessException){
// 自定义添加的业务异常
$returnMessage = $e->getMessage();
$httpStatus = config("status.http_status.business_error");
}else if($e instanceof ValidateException){
$returnMessage = $e->getError();
$httpStatus = config("status.http_status.validate_error");
}else if (($e instanceof ClassNotFoundException || $e instanceof RouteNotFoundException) || ($e instanceof HttpException && $e->getStatusCode() == 404)) {
$returnMessage = env('app_debug') ? $e->getMessage() : '当前请求资源不存在,请稍后再试';
$httpStatus = config("status.http_status.not_found");
}else if ($e instanceof Exception || $e instanceof PDOException || $e instanceof InvalidArgumentException || $e instanceof ErrorException || $e instanceof ParseError || $e instanceof TypeError || ($e instanceof HttpException && $e->getStatusCode() == 500)) {
$returnMessage = env('app_debug') ? $e->getMessage() : '系统异常,请稍后再试';
$httpStatus = config("status.http_status.internal_error");
}
if(request()->isAjax()){
return show($returnCode, $returnMessage, $returnData, $httpStatus);
}else{
if($httpStatus == config("status.http_status.not_found")){
$errorUrl = 'public/error/admin/404.html';
}else{
$errorUrl = 'public/error/admin/error.html';
}
return view(root_path() . $errorUrl, ['e'=>$returnMessage], $httpStatus);
}
}
}
以上代码中返回的错误信息e
,需要在错误页面(404,error)显示:
<p class="error-message">{$e ?? ''}p>
异常检测分浏览器页面访问异常
和api接口返回异常
,还需要检查开发模式
和线上模式
。
Tips:
api
接口可以使用Postman
工具模拟,添加Headers
:
Content-Type
为application/x-www-form-urlencoded
X-Requested-With
为xmlhttprequest