API 接口设计规范——RESTful 最佳实践

API 接口设计规范------RESTful 最佳实践

作者: CaoZH

日期: 2025-06-15

本文为原创教程


当你和团队一起开发时,API 接口设计是否规范直接影响前后端的协作效率。一份好的 API 规范,能让前端开发者不用问"这个接口返回什么字段"就能直接开发。

本文总结了一套经过多个项目验证的 RESTful API 设计规范,涵盖命名、状态码、分页、错误处理等关键方面。

一、基础规范

URL 命名

markdown 复制代码
## 核心原则
- 使用名词复数:/users 而非 /user
- 使用小写 + 连字符:/order-items 而非 /orderItems
- 使用版本号:/api/v1/users
- 层级关系用路径表示:/users/123/orders

## 示例
GET    /api/v1/users              # 用户列表
GET    /api/v1/users/123          # 单个用户
POST   /api/v1/users              # 创建用户
PUT    /api/v1/users/123          # 更新用户
DELETE /api/v1/users/123          # 删除用户
GET    /api/v1/users/123/orders   # 用户的订单列表

HTTP 方法语义

markdown 复制代码
| 方法 | 语义 | 是否幂等 | 请求体 | 响应体 |
|------|------|---------|--------|--------|
| GET | 查询 | ✅ | 无 | 数据 |
| POST | 创建 | ❌ | 有 | 创建成功的资源 |
| PUT | 全量更新 | ✅ | 有 | 更新后的资源 |
| PATCH | 部分更新 | ✅ | 有 | 更新后的资源 |
| DELETE | 删除 | ✅ | 无 | 空/删除状态 |

二、统一响应格式

成功响应

json 复制代码
// 单条数据
{
  "code": 200,
  "message": "操作成功",
  "data": {
    "id": 1,
    "username": "admin",
    "email": "admin@example.com",
    "createdAt": "2025-06-15T10:00:00Z"
  }
}

// 列表数据
{
  "code": 200,
  "message": "查询成功",
  "data": {
    "list": [
      { "id": 1, "username": "admin" },
      { "id": 2, "username": "test" }
    ],
    "total": 100,
    "page": 1,
    "pageSize": 20
  }
}

错误响应

json 复制代码
// 参数错误
{
  "code": 400,
  "message": "参数校验失败",
  "errors": [
    { "field": "email", "message": "邮箱格式不正确" },
    { "field": "age", "message": "年龄必须大于0" }
  ]
}

// 未授权
{
  "code": 401,
  "message": "未登录或 Token 已过期"
}

// 无权限
{
  "code": 403,
  "message": "权限不足"
}

// 资源不存在
{
  "code": 404,
  "message": "用户不存在"
}

// 服务端错误
{
  "code": 500,
  "message": "服务器内部错误,请稍后重试"
}

三、状态码使用指南

markdown 复制代码
## 2xx --- 成功
| 状态码 | 含义 | 使用场景 |
|--------|------|---------|
| 200 | OK | GET 查询成功,PUT/PATCH 更新成功 |
| 201 | Created | POST 创建成功 |
| 204 | No Content | DELETE 删除成功 |

## 3xx --- 重定向
| 状态码 | 含义 | 使用场景 |
|--------|------|---------|
| 301 | Moved Permanently | 资源永久迁移 |
| 302 | Found | 临时重定向 |
| 304 | Not Modified | 缓存有效 |

## 4xx --- 客户端错误
| 状态码 | 含义 | 使用场景 |
|--------|------|---------|
| 400 | Bad Request | 参数错误、格式错误 |
| 401 | Unauthorized | 未登录、Token 失效 |
| 403 | Forbidden | 已登录但无权限 |
| 404 | Not Found | 资源不存在 |
| 405 | Method Not Allowed | 接口不支持该 HTTP 方法 |
| 409 | Conflict | 资源冲突(用户名重复等) |
| 422 | Unprocessable Entity | 业务校验不通过 |
| 429 | Too Many Requests | 请求频率过高 |

## 5xx --- 服务端错误
| 状态码 | 含义 | 使用场景 |
|--------|------|---------|
| 500 | Internal Server Error | 服务器内部错误 |
| 502 | Bad Gateway | 上游服务不可用 |
| 503 | Service Unavailable | 服务暂时不可用 |
| 504 | Gateway Timeout | 上游服务超时 |

四、分页规范

请求

http 复制代码
GET /api/v1/users?page=1&pageSize=20&sort=-createdAt&status=active
参数 类型 默认值 说明
page int 1 页码
pageSize int 20 每页条数(最大 100)
sort string -createdAt 排序字段,- 前缀表示降序
其他 - - 查询过滤条件

响应

json 复制代码
{
  "code": 200,
  "message": "查询成功",
  "data": {
    "list": [...],
    "total": 1000,
    "page": 1,
    "pageSize": 20,
    "totalPages": 50
  }
}

五、认证与鉴权

http 复制代码
# 请求头中携带 Token
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
java 复制代码
// Spring Boot 过滤器示例
@Component
public class JwtAuthFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) {

        String token = extractToken(request);
        if (token != null) {
            try {
                Claims claims = JwtUtil.parseToken(token);
                request.setAttribute("userId", claims.get("userId"));
                request.setAttribute("username", claims.get("sub"));
            } catch (Exception e) {
                response.setStatus(401);
                return;
            }
        }
        chain.doFilter(request, response);
    }

    private String extractToken(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            return header.substring(7);
        }
        return null;
    }
}

六、接口文档

Swagger / Knife4j 配置

xml 复制代码
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>4.3.0</version>
</dependency>
java 复制代码
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(new ApiInfoBuilder()
                .title("用户管理系统 API")
                .version("v1")
                .build())
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.example.controller"))
            .paths(PathSelectors.any())
            .build();
    }
}

七、最佳实践清单

markdown 复制代码
## 设计阶段
□ 使用名词复数命名资源
□ 版本号放在 URL 中
□ 设计好后再开发
□ 前后端先约定接口格式

## 开发阶段
□ 统一响应格式 { code, message, data }
□ 正确使用 HTTP 状态码
□ 所有接口都要有认证
□ 参数校验并返回清晰的错误信息
□ 分页接口统一参数名
□ 敏感字段不返回到前端(密码等)

## 运维阶段
□ 接口添加请求频率限制
□ 记录关键接口的访问日志
□ 提供 API 文档(Swagger)
□ 添加健康检查接口 /api/health

八、总结

好的 API 设计应该让调用者不用看文档也能猜到接口怎么用

markdown 复制代码
## 核心原则

1. 一致性 --- 命名、格式、错误处理保持统一
2. 简单性 --- URL 路径不超过三级
3. 安全性 --- 每个接口都需要鉴权
4. 可扩展性 --- 预留版本号,响应格式可扩展
5. 自文档化 --- 符合 REST 语义,一看就懂

首发于 CaoZH 的笔记