Skip to content

RESTful API 设计最佳实践

API(应用程序编程接口)是前后端通信的桥梁,良好的API设计能够提高开发效率、降低维护成本并提升用户体验。本文将介绍RESTful API的设计原则和最佳实践。

RESTful API 基本原则

1. 使用HTTP动词表示操作

HTTP动词操作示例
GET获取资源GET /users
POST创建资源POST /users
PUT完整更新资源PUT /users/1
PATCH部分更新资源PATCH /users/1
DELETE删除资源DELETE /users/1

2. 使用名词表示资源

http
# 好的设计
GET /users        # 获取用户列表
GET /users/1      # 获取特定用户
POST /users       # 创建新用户
PUT /users/1      # 更新用户
DELETE /users/1   # 删除用户

# 避免的设计
GET /getAllUsers
GET /deleteUser?id=1
POST /createUser
GET /updateUser?id=1

3. 使用复数形式表示资源集合

http
# 推荐
GET /users
GET /products
GET /orders

# 不推荐
GET /user
GET /product
GET /order

URL设计

嵌套资源

http
# 获取特定用户的订单
GET /users/123/orders

# 获取特定订单的项目
GET /orders/456/items

# 嵌套层级不应超过2层
# 推荐
GET /users/123/orders?product=456

# 不推荐
GET /users/123/orders/456/items

查询参数

http
# 分页
GET /users?page=1&limit=20

# 排序
GET /users?sort=createdAt:desc

# 过滤
GET /users?status=active&role=admin

# 搜索
GET /users?q=john&fields=name,email

# 字段选择
GET /users?fields=id,name,email

状态码使用

成功状态码

状态码含义使用场景
200 OK请求成功GET、PATCH、PUT、DELETE
201 Created资源创建成功POST
202 Accepted请求已接受,正在处理异步操作
204 No Content请求成功,无返回内容DELETE

客户端错误状态码

状态码含义使用场景
400 Bad Request请求参数错误参数验证失败
401 Unauthorized未认证缺少认证信息
403 Forbidden无权限认证但无权限访问
404 Not Found资源不存在请求的资源不存在
409 Conflict请求冲突资源状态冲突
422 Unprocessable Entity请求格式正确但语义错误验证失败

服务器错误状态码

状态码含义使用场景
500 Internal Server Error服务器内部错误代码异常
502 Bad Gateway网关错误上游服务错误
503 Service Unavailable服务不可用服务维护或过载

响应格式设计

统一响应结构

json
{
  "success": true,
  "data": {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com"
  },
  "message": "请求成功",
  "timestamp": "2023-07-01T12:00:00Z"
}

列表响应结构

json
{
  "success": true,
  "data": {
    "items": [
      {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com"
      },
      {
        "id": 2,
        "name": "Jane Smith",
        "email": "jane@example.com"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 100,
      "totalPages": 5
    }
  },
  "message": "获取成功",
  "timestamp": "2023-07-01T12:00:00Z"
}

错误响应结构

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数验证失败",
    "details": [
      {
        "field": "email",
        "message": "邮箱格式不正确"
      },
      {
        "field": "password",
        "message": "密码长度至少为8位"
      }
    ]
  },
  "timestamp": "2023-07-01T12:00:00Z"
}

认证与授权

JWT认证

javascript
// 登录接口
POST /api/auth/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "password123"
}

// 响应
{
  "success": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expiresIn": 3600
  }
}

// 受保护的API
GET /api/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

API密钥认证

javascript
// 请求头
GET /api/products
X-API-Key: abc123def456

版本管理

URL版本控制

http
# 版本1
GET /api/v1/users

# 版本2
GET /api/v2/users

请求头版本控制

http
GET /api/users
Accept: application/vnd.myapi.v1+json

查询参数版本控制

http
GET /api/users?version=1

性能优化

分页

http
GET /api/users?page=2&limit=20

# 响应
{
  "data": [...],
  "pagination": {
    "page": 2,
    "limit": 20,
    "total": 100,
    "next": "https://api.example.com/users?page=3&limit=20",
    "prev": "https://api.example.com/users?page=1&limit=20"
  }
}

缓存

http
GET /api/products/123
Cache-Control: max-age=3600
ETag: "abc123"

# 条件请求
GET /api/products/123
If-None-Match: "abc123"

限流

http
# 响应头
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1625097600

# 超过限制
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1625097600
Retry-After: 3600

安全实践

HTTPS

始终使用HTTPS加密通信,防止数据在传输过程中被窃听或篡改。

输入验证

javascript
// 示例:使用Joi进行输入验证
const Joi = require('joi');

const userSchema = Joi.object({
  name: Joi.string().min(2).max(50).required(),
  email: Joi.string().email().required(),
  password: Joi.string().min(8).required(),
  role: Joi.string().valid('user', 'admin').default('user')
});

// 在路由中使用
app.post('/users', (req, res) => {
  const { error, value } = userSchema.validate(req.body);
  if (error) {
    return res.status(400).json({
      success: false,
      error: {
        code: 'VALIDATION_ERROR',
        message: error.details[0].message
      }
    });
  }
  // 创建用户逻辑...
});

SQL注入防护

javascript
// 易受SQL注入攻击
const query = `SELECT * FROM users WHERE email = '${req.body.email}'`;

// 安全的参数化查询
const query = 'SELECT * FROM users WHERE email = ?';
db.query(query, [req.body.email]);

敏感信息处理

javascript
// 不应返回敏感信息
const user = {
  id: 1,
  name: 'John Doe',
  email: 'john@example.com',
  // 不应返回密码
  // password: 'hashedpassword123'
};

// 修改密码后不应返回完整用户信息
app.patch('/users/:id/password', (req, res) => {
  // 修改密码逻辑...
  res.status(204).send(); // No Content
});

文档生成

OpenAPI/Swagger规范

yaml
openapi: 3.0.0
info:
  title: 用户管理API
  version: 1.0.0
  description: 用户管理相关接口

paths:
  /users:
    get:
      summary: 获取用户列表
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      items:
                        type: array
                        items:
                          $ref: '#/components/schemas/User'
                      pagination:
                        $ref: '#/components/schemas/Pagination'
    post:
      summary: 创建用户
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: 创建成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    $ref: '#/components/schemas/User'

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
          format: email
        role:
          type: string
          enum: [user, admin]
        createdAt:
          type: string
          format: date-time
    CreateUserRequest:
      type: object
      required:
        - name
        - email
        - password
      properties:
        name:
          type: string
        email:
          type: string
          format: email
        password:
          type: string
          minLength: 8
        role:
          type: string
          enum: [user, admin]
          default: user
    Pagination:
      type: object
      properties:
        page:
          type: integer
        limit:
          type: integer
        total:
          type: integer
        totalPages:
          type: integer

测试策略

单元测试

javascript
// 测试API端点
describe('User API', () => {
  test('should get user list', async () => {
    const response = await request(app)
      .get('/api/users')
      .expect(200);
    
    expect(response.body.success).toBe(true);
    expect(response.body.data.items).toBeInstanceOf(Array);
    expect(response.body.data.pagination).toBeDefined();
  });
  
  test('should create user', async () => {
    const userData = {
      name: 'Test User',
      email: 'test@example.com',
      password: 'password123'
    };
    
    const response = await request(app)
      .post('/api/users')
      .send(userData)
      .expect(201);
    
    expect(response.body.success).toBe(true);
    expect(response.body.data.name).toBe(userData.name);
    expect(response.body.data.email).toBe(userData.email);
    expect(response.body.data).not.toHaveProperty('password');
  });
});

总结

良好的API设计是构建可扩展、可维护应用程序的关键。通过遵循RESTful原则、使用统一的响应格式、实现适当的认证授权机制以及提供清晰的文档,我们可以创建出既易于使用又便于维护的API。

记住,API设计不仅仅是技术问题,更是用户体验的一部分。一个设计良好的API可以让前端开发者更加高效地工作,减少沟通成本,提高开发效率。


继续探索API设计的更多实践,构建优秀的后端服务!