y.y
Published on

现代 Web API 设计完全指南

现代 Web API 设计完全指南

目录

  1. 简介
  2. 核心设计原则
  3. URL 设计
  4. HTTP 方法
  5. 请求与响应
  6. 错误处理
  7. 安全性
  8. 性能优化
  9. 文档化
  10. 最佳实践示例

简介

Web API 是现代应用程序的核心组件,它为前端应用、移动应用和第三方集成提供了统一的数据访问层。设计一个优秀的 API 不仅需要考虑功能实现,还需要注重可用性、可维护性和扩展性。本指南将详细介绍如何设计一个符合现代标准的 Web API。

核心设计原则

1. RESTful 架构

  • 以资源为中心的设计
  • 无状态通信
  • 缓存支持
  • 统一接口
  • 分层系统

2. 一致性

  • 命名约定保持一致
  • 数据格式统一
  • 错误处理标准化
  • 响应结构保持一致

3. 版本控制

/api/v1/resources
/api/v2/resources

建议:

  • 在 URL 中使用主版本号
  • 使用 Header 处理次要版本更新
  • 保持向后兼容性
  • 提供版本升级指南

URL 设计

资源命名

✅ 好的设计:
GET /api/v1/users
GET /api/v1/user-groups

❌ 避免的设计:
GET /api/v1/getUsers
GET /api/v1/user_groups

资源关系

GET /api/v1/users/:id/posts                 # 获取用户的所有文章
GET /api/v1/users/:id/posts/:post_id        # 获取用户的特定文章

查询参数规范

# 分页
GET /api/v1/users?page=1&per_page=20

# 排序
GET /api/v1/users?sort=created_at:desc

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

# 字段选择
GET /api/v1/users?fields=id,username,email

HTTP 方法

标准用法

GET     /users          # 获取用户列表
GET     /users/:id      # 获取单个用户
POST    /users          # 创建用户
PUT     /users/:id      # 完整更新用户
PATCH   /users/:id      # 部分更新用户
DELETE  /users/:id      # 删除用户

状态码使用

  • 200 OK:成功的 GET、PUT、PATCH 请求
  • 201 Created:成功的 POST 请求
  • 204 No Content:成功的 DELETE 请求
  • 400 Bad Request:请求格式错误
  • 401 Unauthorized:未提供认证信息
  • 403 Forbidden:无权限访问
  • 404 Not Found:资源不存在
  • 409 Conflict:资源冲突
  • 429 Too Many Requests:请求频率超限
  • 500 Internal Server Error:服务器错误

请求与响应

请求格式

POST /api/v1/users
{
  "username": "johndoe",
  "email": "john@example.com",
  "role": "user"
}

响应格式

{
  "data": {
    "id": "123",
    "username": "johndoe",
    "email": "john@example.com",
    "role": "user",
    "created_at": "2024-11-16T10:00:00Z"
  },
  "meta": {
    "request_id": "req_abc123"
  }
}

列表响应

{
  "data": [...],
  "meta": {
    "total": 100,
    "page": 1,
    "per_page": 20,
    "total_pages": 5
  },
  "links": {
    "self": "/api/v1/users?page=1",
    "next": "/api/v1/users?page=2",
    "prev": null
  }
}

错误处理

错误响应格式

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid email format",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address"
      }
    ],
    "request_id": "req_abc123",
    "timestamp": "2024-11-16T10:00:00Z"
  }
}

错误码设计

enum ErrorCode {
  VALIDATION_ERROR = 'VALIDATION_ERROR',
  AUTHENTICATION_ERROR = 'AUTHENTICATION_ERROR',
  AUTHORIZATION_ERROR = 'AUTHORIZATION_ERROR',
  RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',
  RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED',
  INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR'
}

安全性

认证

// JWT 认证示例
app.use((req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) {
    return res.status(401).json({
      error: {
        code: 'AUTHENTICATION_ERROR',
        message: 'No token provided'
      }
    });
  }
  // 验证 token...
});

速率限制

import rateLimit from 'express-rate-limit';

const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100, // 限制100次请求
  message: {
    error: {
      code: 'RATE_LIMIT_EXCEEDED',
      message: 'Too many requests'
    }
  }
});

app.use('/api/', apiLimiter);

性能优化

缓存控制

// 缓存中间件示例
app.use((req, res, next) => {
  if (req.method === 'GET') {
    res.setHeader('Cache-Control', 'public, max-age=300'); // 5分钟缓存
    res.setHeader('ETag', generateETag(req));
  }
  next();
});

压缩

import compression from 'compression';

app.use(compression({
  filter: (req, res) => {
    return req.headers['accept-encoding']?.includes('gzip');
  }
}));

文档化

OpenAPI 规范示例

openapi: 3.0.0
info:
  title: User API
  version: 1.0.0
paths:
  /users:
    get:
      summary: Get users list
      parameters:
        - name: page
          in: query
          schema:
            type: integer
        - name: per_page
          in: query
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsersList'

最佳实践示例

完整的用户 API 实现

import express from 'express';
import { validateRequest } from './middleware/validation';
import { authenticate } from './middleware/auth';
import { rateLimit } from './middleware/rate-limit';

const router = express.Router();

// 获取用户列表
router.get('/users', 
  authenticate,
  rateLimit,
  async (req, res) => {
    const { page = 1, per_page = 20, sort, fields } = req.query;
    try {
      const users = await UserService.list({
        page: Number(page),
        perPage: Number(per_page),
        sort,
        fields: fields?.split(',')
      });
      
      res.json({
        data: users.items,
        meta: {
          total: users.total,
          page: users.page,
          per_page: users.perPage,
          total_pages: Math.ceil(users.total / users.perPage)
        }
      });
    } catch (error) {
      next(error);
    }
  }
);

// 创建用户
router.post('/users',
  authenticate,
  validateRequest(createUserSchema),
  async (req, res) => {
    try {
      const user = await UserService.create(req.body);
      res.status(201).json({
        data: user
      });
    } catch (error) {
      next(error);
    }
  }
);

// 错误处理中间件
router.use((error, req, res, next) => {
  console.error(error);
  res.status(getStatusCode(error)).json({
    error: {
      code: error.code || 'INTERNAL_SERVER_ERROR',
      message: error.message,
      details: error.details,
      request_id: req.id,
      timestamp: new Date().toISOString()
    }
  });
});

结语

设计优秀的 Web API 是一个持续改进的过程。遵循这些最佳实践可以帮助你构建出更易用、更可靠、更易维护的 API。记住:

  • 保持简单性和一致性
  • 注重安全性和性能
  • 提供完善的文档
  • 收集用户反馈并持续改进

希望本指南能够帮助你设计出更好的 API!