340 lines
5.8 KiB
Markdown
340 lines
5.8 KiB
Markdown
# API 响应格式说明
|
||
|
||
## 统一响应格式
|
||
|
||
所有 API 接口现在都返回统一的格式:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "Success",
|
||
"data": {}
|
||
}
|
||
```
|
||
|
||
## 字段说明
|
||
|
||
### code (Number)
|
||
- **0**: 成功
|
||
- **400**: 请求参数错误
|
||
- **401**: 未授权/Token 无效
|
||
- **403**: 禁止访问
|
||
- **404**: 资源不找到
|
||
- **409**: 冲突(如重复数据)
|
||
- **500**: 服务器内部错误
|
||
|
||
### message (String)
|
||
- 操作结果的描述信息
|
||
- 成功时通常为 "Success" 或具体的成功消息
|
||
- 失败时为错误描述
|
||
|
||
### data (Object | Array | null)
|
||
- 成功时包含返回的数据
|
||
- 失败时为 null
|
||
|
||
## 响应示例
|
||
|
||
### 成功响应
|
||
|
||
#### 1. 登录成功
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "Login successful",
|
||
"data": {
|
||
"user": {
|
||
"id": "uuid",
|
||
"nickname": "用户昵称",
|
||
"avatar": "https://...",
|
||
"language": "zh",
|
||
"balance": 0.00
|
||
},
|
||
"token": "Bearer eyJhbGc...",
|
||
"refreshToken": "eyJhbGc..."
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. 获取列表
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "Success",
|
||
"data": {
|
||
"items": [
|
||
{ "id": 1, "name": "Item 1" },
|
||
{ "id": 2, "name": "Item 2" }
|
||
],
|
||
"pagination": {
|
||
"total": 100,
|
||
"page": 1,
|
||
"limit": 10,
|
||
"totalPages": 10
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. 创建资源
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "Created successfully",
|
||
"data": {
|
||
"id": "uuid",
|
||
"name": "New Item",
|
||
"createdAt": "2025-12-05T12:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4. 删除资源
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "Deleted successfully",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
### 错误响应
|
||
|
||
#### 1. 参数验证错误 (400)
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "Invalid input data",
|
||
"data": null,
|
||
"errors": [
|
||
{
|
||
"field": "email",
|
||
"message": "Email is required"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 2. 未授权 (401)
|
||
```json
|
||
{
|
||
"code": 401,
|
||
"message": "Invalid or expired token",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
#### 3. 禁止访问 (403)
|
||
```json
|
||
{
|
||
"code": 403,
|
||
"message": "Insufficient permissions",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
#### 4. 资源不存在 (404)
|
||
```json
|
||
{
|
||
"code": 404,
|
||
"message": "Resource not found",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
#### 5. 服务器错误 (500)
|
||
```json
|
||
{
|
||
"code": 500,
|
||
"message": "Internal server error",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
## 实现方式
|
||
|
||
### 方式 1: 使用响应工具函数
|
||
|
||
```javascript
|
||
const { sendSuccess, sendError, sendPaginated } = require('../utils/response');
|
||
|
||
// 成功响应
|
||
sendSuccess(res, data, 'Operation successful');
|
||
|
||
// 错误响应
|
||
sendError(res, 'Error message', 400, 400);
|
||
|
||
// 分页响应
|
||
sendPaginated(res, items, total, page, limit, 'Success');
|
||
```
|
||
|
||
### 方式 2: 自动转换(推荐)
|
||
|
||
系统已配置响应格式化中间件,会自动转换所有响应:
|
||
|
||
```javascript
|
||
// 旧格式(会自动转换)
|
||
res.json({
|
||
success: true,
|
||
data: { ... }
|
||
});
|
||
|
||
// 自动转换为
|
||
{
|
||
code: 0,
|
||
message: "Success",
|
||
data: { ... }
|
||
}
|
||
```
|
||
|
||
## 前端使用
|
||
|
||
### 判断请求是否成功
|
||
|
||
```javascript
|
||
const response = await api.login(code);
|
||
|
||
// 方式 1: 判断 code
|
||
if (response.code === 0) {
|
||
// 成功
|
||
console.log(response.data);
|
||
} else {
|
||
// 失败
|
||
console.error(response.message);
|
||
}
|
||
|
||
// 方式 2: 兼容旧格式
|
||
const isSuccess = (response.code === 0) || (response.success === true);
|
||
if (isSuccess) {
|
||
// 成功
|
||
}
|
||
```
|
||
|
||
### 错误处理
|
||
|
||
```javascript
|
||
try {
|
||
const response = await api.someRequest();
|
||
|
||
if (response.code !== 0) {
|
||
// 显示错误消息
|
||
uni.showToast({
|
||
title: response.message,
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 处理成功数据
|
||
const data = response.data;
|
||
|
||
} catch (error) {
|
||
console.error('Request failed:', error);
|
||
}
|
||
```
|
||
|
||
## 迁移指南
|
||
|
||
### 从旧格式迁移
|
||
|
||
如果你的代码使用旧格式 `{ success: true, data: {} }`:
|
||
|
||
#### 1. 更新判断逻辑
|
||
|
||
```javascript
|
||
// 旧代码
|
||
if (data.success) {
|
||
// ...
|
||
}
|
||
|
||
// 新代码
|
||
if (data.code === 0) {
|
||
// ...
|
||
}
|
||
|
||
// 或兼容两种格式
|
||
if (data.code === 0 || data.success === true) {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
#### 2. 更新错误处理
|
||
|
||
```javascript
|
||
// 旧代码
|
||
if (!data.success) {
|
||
console.error(data.error.message);
|
||
}
|
||
|
||
// 新代码
|
||
if (data.code !== 0) {
|
||
console.error(data.message);
|
||
}
|
||
```
|
||
|
||
#### 3. 更新数据访问
|
||
|
||
```javascript
|
||
// 数据访问方式不变
|
||
const user = data.data.user;
|
||
const items = data.data.items;
|
||
```
|
||
|
||
## 注意事项
|
||
|
||
1. **code 为 0 表示成功**,其他值表示失败
|
||
2. **message** 字段始终存在,包含操作结果描述
|
||
3. **data** 字段在失败时为 null
|
||
4. 开发环境下,错误响应可能包含额外的 **error** 字段用于调试
|
||
5. HTTP 状态码与 code 字段可能不同,建议以 code 字段为准
|
||
|
||
## 相关文件
|
||
|
||
- `backend/src/middleware/responseFormatter.js` - 响应格式化中间件
|
||
- `backend/src/utils/response.js` - 响应工具函数
|
||
- `backend/src/middleware/errorHandler.js` - 错误处理中间件
|
||
|
||
## 测试
|
||
|
||
### 测试成功响应
|
||
|
||
```bash
|
||
curl http://localhost:3000/api/v1/categories
|
||
|
||
# 期望输出
|
||
{
|
||
"code": 0,
|
||
"message": "Success",
|
||
"data": [...]
|
||
}
|
||
```
|
||
|
||
### 测试错误响应
|
||
|
||
```bash
|
||
curl http://localhost:3000/api/v1/services/invalid-id
|
||
|
||
# 期望输出
|
||
{
|
||
"code": 404,
|
||
"message": "Service not found",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
## 常见问题
|
||
|
||
### Q: 为什么要统一响应格式?
|
||
A: 统一的响应格式让前端更容易处理 API 响应,减少判断逻辑,提高代码可维护性。
|
||
|
||
### Q: code 和 HTTP 状态码有什么区别?
|
||
A: HTTP 状态码是传输层的状态,code 是业务层的状态。建议前端主要使用 code 判断业务成功与否。
|
||
|
||
### Q: 如何处理分页数据?
|
||
A: 分页数据包含在 data 对象中,包括 items 数组和 pagination 对象。
|
||
|
||
### Q: 旧代码会自动转换吗?
|
||
A: 是的,响应格式化中间件会自动转换旧格式到新格式,无需修改现有控制器代码。
|
||
|
||
### Q: 如何在开发环境查看详细错误?
|
||
A: 开发环境下,错误响应会包含额外的 error 对象,包含详细的错误信息和堆栈跟踪。
|