1. 引言
RESTful API 是当前 Web 服务接口设计的主流风格之一,它基于 HTTP 协议,强调资源、统一接口和无状态等核心思想。本文将从设计原则出发,结合丰富的代码实例,介绍如何设计、实现和测试一套规范的 RESTful API。
2. REST 核心概念
REST(Representational State Transfer,表述性状态转移)由 Roy Fielding 在 2000 年提出。它并不是一种协议,而是一种架构风格,核心围绕「资源」展开。
- 资源:一切可被命名和寻址的事物,例如用户、订单、商品。
- 统一接口:通过 HTTP 方法表达对资源的操作。
- 无状态:服务端不保存客户端上下文,每次请求都携带完整信息。
- 表述:资源可以有多种表述形式,如 JSON、XML。
3. 资源设计与 URL 规范
URL 是资源的唯一标识。设计时应使用名词而非动词,并遵循层级关系。
GET /api/users # 获取用户列表 POST /api/users # 创建用户 GET /api/users/1 # 获取单个用户 PUT /api/users/1 # 整体更新用户 PATCH /api/users/1 # 部分更新用户 DELETE /api/users/1 # 删除用户 GET /api/users/1/orders # 获取用户的订单列表设计要点:
- 使用小写字母和连字符,例如
order-items而非orderItems。 - 避免在 URL 中使用动词,如
/api/getUser应改为/api/users/1。 - 使用复数名词表示资源集合。
4. HTTP 方法与状态码
合理使用 HTTP 方法能让接口语义更清晰。下面是一个常见映射表:
| HTTP 方法 | 语义 | 典型状态码 |
|---|---|---|
| GET | 查询资源 | 200 OK |
| POST | 创建资源 | 201 Created |
| PUT | 整体更新 | 200 OK |
| PATCH | 部分更新 | 200 OK |
| DELETE | 删除资源 | 204 No Content |
常见错误状态码:
- 400 Bad Request:请求参数错误。
- 401 Unauthorized:未认证。
- 403 Forbidden:已认证但无权限。
- 404 Not Found:资源不存在。
- 500 Internal Server Error:服务端异常。
5. 代码实例:基于 Spring Boot 实现
下面以 Java 的 Spring Boot 为例,演示一个用户资源的 RESTful API 实现。
5.1 实体类
public class User { private Long id; private String name; private String email; public User() {} public User(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } // getter 和 setter 省略 }5.2 控制器
@RestController @RequestMapping("/api/users") public class UserController { private final List<User> users = new ArrayList<>(); private Long nextId = 1L; @GetMapping public List<User> listUsers() { return users; } @GetMapping("/{id}") public ResponseEntity<User> getUser(@PathVariable Long id) { return users.stream() .filter(u -> u.getId().equals(id)) .findFirst() .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } @PostMapping public ResponseEntity<User> createUser(@RequestBody User user) { user.setId(nextId++); users.add(user); return ResponseEntity.status(HttpStatus.CREATED).body(user); } @PutMapping("/{id}") public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) { return users.stream() .filter(u -> u.getId().equals(id)) .findFirst() .map(u -> { u.setName(user.getName()); u.setEmail(user.getEmail()); return ResponseEntity.ok(u); }) .orElse(ResponseEntity.notFound().build()); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser(@PathVariable Long id) { boolean removed = users.removeIf(u -> u.getId().equals(id)); return removed ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build(); } }6. 代码实例:基于 Node.js Express 实现
如果团队使用 JavaScript 技术栈,可以用 Express 快速搭建同样的接口。
const express = require('express'); const app = express(); app.use(express.json()); let users = []; let nextId = 1; // 获取用户列表 app.get('/api/users', (req, res) => { res.json(users); }); // 获取单个用户 app.get('/api/users/:id', (req, res) => { const user = users.find(u => u.id === Number(req.params.id)); if (!user) { return res.status(404).json({ message: '用户不存在' }); } res.json(user); }); // 创建用户 app.post('/api/users', (req, res) => { const user = { id: nextId++, ...req.body }; users.push(user); res.status(201).json(user); }); // 更新用户 app.put('/api/users/:id', (req, res) => { const index = users.findIndex(u => u.id === Number(req.params.id)); if (index === -1) { return res.status(404).json({ message: '用户不存在' }); } users[index] = { ...users[index], ...req.body }; res.json(users[index]); }); // 删除用户 app.delete('/api/users/:id', (req, res) => { const index = users.findIndex(u => u.id === Number(req.params.id)); if (index === -1) { return res.status(404).json({ message: '用户不存在' }); } users.splice(index, 1); res.status(204).send(); }); app.listen(3000, () => { console.log('服务已启动:http://localhost:3000'); });7. 统一响应格式与错误处理
为了让前端调用更一致,建议封装统一的响应结构。下面是一个 JSON 响应示例:
{ "code": 0, "message": "success", "data": { "id": 1, "name": "张三", "email": "zhangsan@example.com" } }错误响应示例:
{ "code": 404, "message": "用户不存在", "data": null }在 Spring Boot 中,可以用@RestControllerAdvice统一处理异常:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<Map<String, Object>> handleNotFound(ResourceNotFoundException ex) { Map<String, Object> body = new HashMap<>(); body.put("code", 404); body.put("message", ex.getMessage()); body.put("data", null); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body); } }8. 认证与版本控制
生产环境的 API 通常需要认证和版本管理。
8.1 认证方式
- Token 认证:客户端在请求头携带
Authorization: Bearer <token>。 - OAuth 2.0:适用于第三方授权场景。
- API Key:简单场景下使用,但安全性较弱。
8.2 版本控制
常见做法是在 URL 中加入版本号:
/api/v1/users /api/v2/users也可以使用请求头Accept: application/vnd.example.v2+json进行版本协商。
9. 测试与调试
接口完成后,可以使用 curl 或 Postman 进行验证。下面是一些 curl 示例:
# 创建用户 curl -X POST http://localhost:3000/api/users \ -H "Content-Type: application/json" \ -d '{"name":"李四","email":"lisi@example.com"}' 获取用户列表 curl http://localhost:3000/api/users 获取单个用户 curl http://localhost:3000/api/users/1 更新用户 curl -X PUT http://localhost:3000/api/users/1 -H "Content-Type: application/json" -d '{"name":"李四改","email":"lisi_new@example.com"}' 删除用户 curl -X DELETE http://localhost:3000/api/users/110. 总结
RESTful API 的核心在于「以资源为中心、以 HTTP 方法为操作手段」。本文从 URL 设计、状态码选择、代码实现、错误处理、认证和测试等角度,结合 Java 与 Node.js 的实例,展示了完整的实践路径。在实际项目中,建议团队先约定统一的接口规范,再结合业务场景灵活调整,从而提升前后端协作效率与接口的可维护性。