### 项目中的自定义异常处理总结 错误页面&API错误
* 前言
* 一、异常分类
* * 1\. 控制器找不到
* 2\. 方法找不到
* 3\. 请求资源不存在
* 4\. 系统內部异常、HTTP异常等
* 二、异常处理
* * 1\. 前置处理
* 2\. 异常处理详细代码
* * (1) 控制器找不到
* (2) 方法找不到
* (3) 请求资源不存在及系统错误异常
* 三、异常检测
# 前言
一般项目中路由分为返回`模板引擎页面`和返回`api接口json数据`,两种方式异常需要返回不同的内容,如果是模板引擎页面遇到异常需要返回错误页面,如果是api接口遇到异常需要返回json数据。
开发模式和上线模式应该返回不同的内容,开发模式应该尽可能返回具体的错误信息,上线模式则不能返回具体的错误信息,一般显示“服务器错误,请稍后重试”类似友好的提示,而不是显示一堆报错代码(既不友好又不安全)。
如果有更好的方法,欢迎提出意见。
# 一、异常分类
## 1\. 控制器找不到
在访问路由时,若控制器不对,需要使用`空控制器`拦截报错。
## 2\. 方法找不到
方法找不到可以修改app目录下的BaseController控制器重写\_\_call方法。
## 3\. 请求资源不存在
自定义拦截报错信息,可以使用Provider自定义错误处理类,重写render方法,根据不同的错误返回不同的数据。
## 4\. 系统內部异常、HTTP异常等
同第3点。
# 二、异常处理
## 1\. 前置处理
> (1)`.env`文件定义`APP_DEBUG`区分开发模式和线上模式,true表示开发模式,false表示线上模式:
~~~php
APP_DEBUG = true
~~~
> (2) 在`app/common.php`文件定义`api`返回的数据格式:
~~~php
function show($status, $message = 'error', $data = [], $httpStatus = 200){$result = ["status" => $status,"message" => $message,"result" => $data];return json($result, $httpStatus);
}
~~~
> (3) 在当前应用下新建一个provider.php,并指定自定义异常处理类:
~~~php
<?php// 容器Provider定义文件
return ['think\exception\Handle' => 'app\\admin\\exception\\Http',
];
~~~
> (4) 定义状态码配置,可以在`config`文件夹下新建`status.php`添加相应的状态码配置:
~~~php
<?phpreturn ["success" => 1,"error" => 0,"http_status" => ["not_found" => 404,"validate_error" => 422,"internal_error" => 500]
];
~~~
> (5) 准备错误页面(404,500等)
## 2\. 异常处理详细代码
### (1) 控制器找不到
在`app/controller`目录新建`Error`类(`文件名固定为Error`):
> `这里需要注意的是,多应用的控制器应该定义在应用文件夹里,这里定义在app目录是为了作用于app下全部应用。`
~~~php
<?phpnamespace 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"));}}
}
~~~
### (2) 方法找不到
在`app/BaseController.php`控制器添加`__call`方法:
~~~php
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"));}}
~~~
### (3) 请求资源不存在及系统错误异常
`app\\admin\\exception\\Http`:
~~~php
<?phpnamespace 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)显示:
~~~html
<p class="error-message">{$e ?? ''}</p>
~~~
# 三、异常检测
异常检测分浏览器`页面访问异常`和`api接口返回异常`,还需要检查`开发模式`和`线上模式`。
1. 控制器不存在
2. 方法不存在
3. 主动抛出异常
4. 系统抛出异常
> Tips:`api`接口可以使用`Postman`工具模拟,添加`Headers`:
> `Content-Type`为`application/x-www-form-urlencoded`
> `X-Requested-With`为`xmlhttprequest`
(博主比较懒就不贴截图了,但是都测试过,同志们自己试一下,算了还是贴一张吧)
- 空白目录
- 使用thinkphp6搭建后端api接口流程
- tp6 uniapp vue 前后端跨域解决方案
- 操作记录
- api00
- 你看看有没有用
- 6666
- Docker安装LNMP环境的详细过程(可部署TP项目)
- LNMP部署thinkphp
- 玩客云Armbian 安装LNMP环境 Docker
- ThinkPHP6项目基操(16.实战部分 redis+token登录)
- ThinkPHP6项目基操(11.实战部分 部署后台静态页面模板及后台登录页面)
- ThinkPHP6项目基操(13.实战部分 项目中的自定义异常处理总结 错误页面API错误)
- ThinkPHP6项目基操(14.实战部分 中间件处理登录流程)
- ThinkPHP6项目基操(12.实战部分 验证码)
- ThinkPHP6项目基操(18.实战部分 表单令牌Token 防CSRF)
- ThinkPHP6项目基操(19.实战部分 Mysql模型事务操作)
- ThinkPHP6项目基操(20.实战部分 数据库操作返回值总结)
- 浏览器端判断当前设备的运行环境
- api
- api异常捕捉
- 写一个中间件
- 统一的参数返回形式
- ThinkPHP6调用模型的方法
- thinkphp6控制器、验证器、模型、service,各层写的内容