# 异常处理
和PHP默认的异常处理不同,ThinkPHP抛出的不是单纯的错误信息,而是一个人性化的错误页面。
- [异常显示](#_4)
- [异常忽略](#_27)
- [异常处理接管](#_42)
- [手动抛出和捕获异常](#_104)
- [HTTP 异常](#HTTP__164)
## 异常显示
在调试模式下,系统默认展示的异常页面:
![](https://box.kancloud.cn/cc2522ef8a6b4c94f2ca1421619e87ca_736x642.png)
> 只有在调试模式下面才能显示具体的错误信息,如果在部署模式下面,你可能看到的是一个简单的提示文字,例如:
![](https://box.kancloud.cn/f97804ae6c541d6e10207e6a565c3e2d_501x125.png)
你可以通过设置`exception_tmpl`配置参数来自定义你的异常页面模板,默认的异常模板位于:
```
thinkphp/tpl/think_exception.tpl
```
你可以在应用配置文件`app.php`中更改异常模板
```
// 自定义异常页面的模板文件
'exception_tmpl' => Env::get('app_path') . 'template/exception.tpl',
```
默认的异常页面会返回`500`状态码,如果是一个`HttpException`异常则会返回HTTP的错误状态码。
## 异常忽略
> #### 本着严谨的原则,`5.0`版本开始默认情况下会对任何错误(包括警告错误)抛出异常,如果不希望如此严谨的抛出异常,可以使用下面方法设置。
默认框架会捕获所有的错误,包括PHP警告级别的错误,你可以在**应用配置文件或者公共文件**中设置要报告的错误级别,例如:
```
// 除了 E_NOTICE,报告其他所有错误
error_reporting(E_ALL ^ E_NOTICE);
```
> 由于错误机制的注册顺序问题,在入口文件中设置错误级别无效。
> 系统产生的异常和错误都是程序的隐患,要尽早排除和解决,而不是掩盖。对于应用自己抛出的异常则做出相应的捕获处理。
## 异常处理接管
框架支持异常处理由开发者自定义类进行接管,需要在应用配置文件`app.php`中配置参数`exception_handle`。
最简单的方式是使用闭包定义异常处理,例如:
```
'exception_handle' => function($e) {
// 参数验证错误
if ($e instanceof \think\exception\ValidateException) {
return json($e->getError(), 422);
}
// 请求异常
if ($e instanceof \think\exception\HttpException && request()->isAjax()) {
return response($e->getMessage(), $e->getStatusCode());
}
},
```
如果需要更复杂的异常处理,可以把`exception_handle`配置定义为异常处理类的名称,例如:
```
// 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle' => '\\app\\common\\exception\\Http',
```
自定义类需要继承`think\exception\Handle`并且实现`render`方法,可以参考如下代码:
```
<?php
namespace app\common\exception;
use Exception;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\ValidateException;
class Http extends Handle
{
public function render(Exception $e)
{
// 参数验证错误
if ($e instanceof ValidateException) {
return json($e->getError(), 422);
}
// 请求异常
if ($e instanceof HttpException && request()->isAjax()) {
return response($e->getMessage(), $e->getStatusCode());
}
// 其他错误交给系统处理
return parent::render($e);
}
}
```
自定义异常处理的主要作用是根据不同的异常类型发送不同的状态码和响应输出格式。
> 需要注意的是,如果自定义异常处理类没有再次调用系统`render`方法的话,配置`http_exception_template`就不再生效,具体可以参考`Handle`类内实现的功能。
## 手动抛出和捕获异常
ThinkPHP大部分情况异常都是自动抛出和捕获的,你也可以手动使用`throw`来抛出一个异常,例如:
```
// 使用think自带异常类抛出异常
throw new \think\Exception('异常消息', 10006);
```
系统提供了一个助手函数简化异常的代码,用法如下:
> ### exception('异常信息','异常代码','异常类')
异常代码默认为`0`,可以根据应用的异常设计来定义,异常类如果不传表示抛出默认的`think\Exception`异常,下面是示例:
```
// 使用助手函数抛出异常
exception('异常消息', 10006);
```
如果需要抛出自定义异常,可以使用:
```
// 抛出自定义异常
exception('异常消息', 10006,'\app\common\exception\NotFoundException');
```
手动捕获异常方式是使用`try-catch`,例如:
```
try {
// 这里是主体代码
} catch (ValidateException $e) {
// 这是进行验证异常捕获
return json($e->getError());
} catch (\Exception $e) {
// 这是进行异常捕获
return json($e->getMessage());
}
```
> 支持使用`try-catch-finally`结构捕获异常。
可以使用PHP的异常捕获进行必要的处理,但需要注意一点,在异常捕获中不要使用`think\Controller`类的error、success和redirect方法,因为上述三个方法会抛出`HttpResponseException`异常,从而影响正常的异常捕获,例如:
```
try{
Db::name('user')->find();
$this->success('执行成功!');
}catch(\Exception $e){
$this->error('执行错误');
}
```
应该改成
```
try{
Db::name('user')->find();
}catch(\Exception $e){
$this->error('执行错误');
}
$this->success('执行成功!');
```
## HTTP 异常
可以使用`\think\exception\HttpException`类来抛出异常
框架提供了一个`abort`助手函数快速抛出一个HTTP异常:
```
<?php
namespace app\index\controller;
use think\Controller;
class Index extends Controller
{
public function index()
{
// 抛出 HTTP 异常
throw new \think\exception\HttpException(404, '异常消息');
}
}
```
系统提供了助手函数`abort`简化HTTP异常的处理,例如:
框架提供了一个`abort`助手函数快速抛出一个HTTP异常:
```
<?php
namespace app\index\controller;
use think\Controller;
class Index extends Controller
{
public function index()
{
// 抛出404异常
abort(404, '页面异常');
}
}
```
如果你的应用是API接口,那么请注意在客户端首先判断HTTP状态码是否正常,然后再进行数据处理,当遇到错误的状态码的话,应该根据状态码自行给出错误提示,或者采用下面的方法进行自定义异常处理。
**部署模式**下一旦抛出了`HttpException`异常,可以定义单独的异常页面模板,只需要在应用配置文件中增加:
```
'http_exception_template' => [
// 定义404错误的模板文件地址
404 => Env::get('app_path') . '404.html',
// 还可以定义其它的HTTP status
401 => Env::get('app_path') . '401.html',
]
```
模板文件支持模板引擎中的标签。
> `http_exception_template`配置仅在部署模式下面生效。
- 序言
- 基础
- 安装
- 开发规范
- 目录结构
- 配置
- 架构
- 架构总览
- 入口文件
- URL访问
- 模块设计
- 命名空间
- 容器和依赖注入
- Facade
- 钩子和行为
- 中间件
- 路由
- 路由定义
- 变量规则
- 路由地址
- 闭包支持
- 路由参数
- 路由缓存
- 跨域请求
- 注解路由
- 路由分组
- MISS路由
- 资源路由
- 快捷路由
- 路由别名
- 路由绑定
- 域名路由
- URL生成
- 控制器
- 控制器定义
- 前置操作
- 跳转和重定向
- 空操作和空控制器
- 分层控制器
- 资源控制器
- 请求
- 请求对象
- 请求信息
- 输入变量
- 请求类型
- HTTP头信息
- 伪静态
- 参数绑定
- 请求缓存
- 响应
- 响应输出
- 响应参数
- 重定向
- 数据库
- 连接数据库
- 查询构造器
- 查询数据
- 添加数据
- 更新数据
- 删除数据
- 查询表达式
- 链式操作
- 聚合查询
- 时间查询
- 高级查询
- 视图查询
- JSON字段
- 子查询
- 原生查询
- 查询事件
- 事务操作
- 监听SQL
- 存储过程
- 数据集
- 分布式数据库
- 模型
- 定义
- 新增
- 更新
- 删除
- 查询
- JSON字段
- 获取器
- 修改器
- 自动时间戳
- 只读字段
- 软删除
- 类型转换
- 数据完成
- 查询范围
- 模型输出
- 模型事件
- 模型关联
- 一对一关联
- 一对多关联
- 远程一对多
- 多对多关联
- 多态关联
- 关联预载入
- 关联统计
- 关联输出
- 视图
- 视图渲染
- 视图赋值
- 视图过滤
- 模板引擎
- 模板
- 变量输出
- 使用函数
- 运算符
- 原样输出
- 模板注释
- 模板布局
- 模板继承
- 包含文件
- 输出替换
- 标签库
- 内置标签
- 循环标签
- 比较标签
- 条件判断
- 资源文件加载
- 标签嵌套
- 原生PHP
- 定义标签
- 标签扩展
- 错误和日志
- 异常处理
- 日志处理
- 调试
- 调试模式
- Trace调试
- 性能调试
- SQL调试
- 变量调试
- 远程调试
- 验证
- 验证器
- 验证规则
- 错误信息
- 验证场景
- 路由验证
- 内置规则
- 独立验证
- 静态调用
- 表单令牌
- 杂项
- 缓存
- Session
- Cookie
- 多语言
- 分页
- 上传
- 命令行
- 启动内置服务器
- 自动生成目录结构
- 创建类库文件
- 生成类库映射文件
- 清除缓存文件
- 生成配置缓存文件
- 生成数据表字段缓存
- 生成路由映射缓存
- 自定义指令
- 扩展库
- 验证码
- 图像处理
- Time
- 数据库迁移工具
- Workerman
- MongoDb
- 单元测试
- 安全和性能
- 安全建议
- 优化建议
- 附录
- 助手函数
- 升级指导
- 更新日志