study-api-v2/docs/异常处理使用指南.md

485 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 异常处理使用指南
## 概述
本项目采用统一的异常处理机制所有API接口的错误都将返回统一的JSON格式。开发者只需要使用简单的方法就能抛出业务异常。
## 快速使用
### 1. 引入异常处理器
```php
use App\Exceptions\Handler;
```
### 2. 基本用法
```php
// 最基本的用法 - 抛出默认错误
Handler::throw('用户不存在');
// 指定错误码
Handler::throw('用户不存在', 404);
// 使用别名方法
Handler::error('参数错误', 400);
// 操作失败
Handler::fail('删除失败');
```
## 可用方法
### Handler::throw($message, $code = 400)
**功能**: 抛出业务异常(主要方法)
```php
Handler::throw('数据不存在', 404);
Handler::throw('权限不足', 403);
Handler::throw('操作失败'); // 默认错误码400
```
### Handler::error($message, $code = 400)
**功能**: throw方法的别名使用习惯更友好
```php
Handler::error('用户名已存在', 409);
Handler::error('参数错误');
```
### Handler::fail($message = '操作失败')
**功能**: 快速抛出操作失败异常
```php
Handler::fail(); // 默认消息:操作失败
Handler::fail('用户创建失败');
Handler::fail('文件上传失败');
```
## 使用场景
### 1. 控制器中使用
```php
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\BaseController;
use App\Exceptions\Handler;
use App\Models\User;
use Illuminate\Http\Request;
class UserController extends BaseController
{
public function show($id)
{
// 参数验证
if (empty($id)) {
Handler::error('用户ID不能为空');
}
// 查找用户
$user = User::find($id);
if (!$user) {
Handler::throw('用户不存在', 404);
}
// 权限检查
if ($user->status === 0) {
Handler::throw('用户已被禁用', 403);
}
return $this->SuccessObject($user);
}
public function store(Request $request)
{
$username = $request->input('username');
// 检查用户名是否存在
if (User::where('username', $username)->exists()) {
Handler::error('用户名已存在', 409);
}
try {
$user = User::create($request->all());
return $this->SuccessObject($user);
} catch (\Exception $e) {
Handler::fail('用户创建失败');
}
}
public function destroy($id)
{
$user = User::find($id);
if (!$user) {
Handler::throw('用户不存在', 404);
}
// 不能删除管理员
if ($user->is_admin) {
Handler::error('不能删除管理员账户', 403);
}
if (!$user->delete()) {
Handler::fail('用户删除失败');
}
return $this->Success(['message' => '删除成功']);
}
}
```
### 2. 服务类中使用
```php
<?php
namespace App\Services;
use App\Exceptions\Handler;
use App\Models\User;
class UserService
{
public function createUser(array $data)
{
// 验证用户名
if (empty($data['username'])) {
Handler::error('用户名不能为空');
}
// 检查重复
if ($this->usernameExists($data['username'])) {
Handler::error('用户名已存在', 409);
}
// 创建用户
try {
return User::create($data);
} catch (\Exception $e) {
Handler::fail('用户创建失败');
}
}
public function updateUserStatus($userId, $status)
{
$user = User::find($userId);
if (!$user) {
Handler::throw('用户不存在', 404);
}
if ($user->is_admin && $status === 0) {
Handler::error('不能禁用管理员账户', 403);
}
$user->status = $status;
if (!$user->save()) {
Handler::fail('状态更新失败');
}
return $user;
}
private function usernameExists($username)
{
return User::where('username', $username)->exists();
}
}
```
### 3. 中间件中使用
```php
<?php
namespace App\Http\Middleware;
use App\Exceptions\Handler;
use Closure;
use Illuminate\Http\Request;
class CheckUserStatus
{
public function handle(Request $request, Closure $next)
{
$user = $request->user();
if (!$user) {
Handler::error('用户未登录', 401);
}
if ($user->status === 0) {
Handler::throw('账户已被禁用', 403);
}
if ($user->deleted) {
Handler::throw('账户不存在', 404);
}
return $next($request);
}
}
```
### 4. 模型中使用
```php
<?php
namespace App\Models;
use App\Exceptions\Handler;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function changePassword($newPassword)
{
if (strlen($newPassword) < 6) {
Handler::error('密码长度不能少于6位');
}
$this->password = bcrypt($newPassword);
if (!$this->save()) {
Handler::fail('密码修改失败');
}
}
public function assignRole($roleId)
{
if ($this->is_admin) {
Handler::error('管理员不能修改角色', 403);
}
$role = Role::find($roleId);
if (!$role) {
Handler::throw('角色不存在', 404);
}
$this->role_id = $roleId;
if (!$this->save()) {
Handler::fail('角色分配失败');
}
}
}
```
## 响应格式
所有异常都会返回统一的JSON格式
### 成功响应
```json
{
"success": true,
"message": "success",
"code": 200,
"data": {
// 具体数据
}
}
```
### 异常响应
```json
{
"success": false,
"message": "具体错误信息",
"code": 400,
"data": null
}
```
### 参数验证错误
```json
{
"success": false,
"message": "参数错误",
"code": 422,
"data": null,
"errors": {
"username": ["用户名不能为空"],
"email": ["邮箱格式不正确"]
}
}
```
## 常用错误码
| 错误码 | 说明 | 使用场景 |
|--------|------|----------|
| 400 | 通用错误 | 默认业务错误 |
| 401 | 未授权 | 用户未登录 |
| 403 | 权限不足 | 没有操作权限 |
| 404 | 资源不存在 | 数据不存在 |
| 409 | 冲突 | 数据已存在 |
| 422 | 参数错误 | 验证失败 |
| 500 | 服务器错误 | 系统异常 |
## 测试异常处理
项目提供了测试接口来验证异常处理(仅开发环境可用):
```bash
# 测试参数验证异常
curl -X POST http://localhost:8000/admin/test/validation \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
# 测试业务异常
curl -X GET http://localhost:8000/admin/test/business-exception \
-H "Authorization: Bearer YOUR_TOKEN"
# 测试参数错误
curl -X GET http://localhost:8000/admin/test/param-error \
-H "Authorization: Bearer YOUR_TOKEN"
# 测试操作失败
curl -X GET http://localhost:8000/admin/test/fail \
-H "Authorization: Bearer YOUR_TOKEN"
# 测试系统异常
curl -X GET http://localhost:8000/admin/test/system-exception \
-H "Authorization: Bearer YOUR_TOKEN"
```
## 最佳实践
### 1. 异常使用原则
- **业务逻辑错误**: 使用 `Handler::throw()``Handler::error()`
- **操作失败**: 使用 `Handler::fail()`
- **参数验证**: 使用Laravel的Validation会自动处理
- **系统错误**: 直接throw Exception会自动处理
### 2. 错误信息编写
- 错误信息要简洁明确
- 面向用户,避免技术术语
- 提供解决建议(如果可能)
```php
// ✅ 好的错误信息
Handler::error('用户名已存在,请尝试其他用户名');
Handler::throw('文件大小不能超过2MB', 413);
// ❌ 不好的错误信息
Handler::error('数据库约束冲突');
Handler::throw('系统错误');
```
### 3. 错误码规范
```php
// 常用错误码
Handler::throw('数据不存在', 404);
Handler::throw('权限不足', 403);
Handler::throw('数据已存在', 409);
Handler::error('参数错误', 400);
```
### 4. 性能考虑
- 不要在循环中频繁抛出异常
- 异常只用于错误处理,不要用于控制程序流程
- 在抛出异常前先做基本检查
```php
// ✅ 好的做法
if (empty($users)) {
return $this->Success([]); // 返回空数据
}
foreach ($users as $user) {
// 正常处理
}
// ❌ 不好的做法
foreach ($users as $user) {
if ($user->invalid) {
Handler::error('用户无效'); // 在循环中抛异常
}
}
```
## 注意事项
1. **生产环境**: 系统异常会隐藏详细信息,只返回"服务器错误"
2. **开发环境**: 系统异常会显示详细的调试信息
3. **日志记录**: 所有异常都会自动记录到日志文件
4. **测试路由**: 仅在开发环境可用,生产环境会自动禁用
## 迁移指南
如果您之前使用的是复杂的异常处理方式,可以按以下方式迁移:
```php
// 之前的方式
throw new BusinessException(ResponseEnum::DATA_NOT_FOUND_ERROR, '用户不存在');
// 现在的方式
Handler::throw('用户不存在', 404);
// 之前的方式
$this->throwBusinessException(ResponseEnum::CLIENT_PARAMETER_ERROR, '参数错误');
// 现在的方式
Handler::error('参数错误');
```
这样大大简化了异常处理的使用,让开发更加便捷!
## API认证中间件
为了解决API项目中认证失败时返回重定向错误的问题我们创建了专用的API认证中间件
### 中间件文件
- `app/Http/Middleware/AdminApiAuthenticate.php` - 专用API认证中间件
### 功能特点
1. **统一的401错误响应**认证失败时返回JSON格式的401错误而不是重定向
2. **支持Sanctum认证**:默认使用`sanctum`守护器进行认证
3. **扩展性强**:可轻松扩展支持其他认证方式
### 响应格式
```json
{
"success": false,
"message": "未授权访问,请先登录",
"code": 401,
"data": null
}
```
### 使用方式
在路由中使用`admin.auth`中间件:
```php
Route::middleware('admin.auth')->group(function () {
// 需要认证的路由
});
```
### 扩展示例
如果需要支持更多认证方式,可以修改中间件:
```php
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? ['sanctum'] : $guards;
// 可以在这里添加其他认证逻辑
// 例如JWT、API Key等
foreach ($guards as $guard) {
if (auth()->guard($guard)->check()) {
auth()->shouldUse($guard);
return $next($request);
}
}
// 自定义认证失败响应
return response()->json([
'success' => false,
'message' => '未授权访问,请先登录',
'code' => 401,
'data' => null
], 401);
}
```