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设计的更多实践,构建优秀的后端服务!