在现代 Web 开发中,RESTful API 已经成为前后端交互的标准方式。一个设计良好的 API 不仅能提高开发效率,还能降低维护成本。本文将深入探讨如何优雅地设计 RESTful API。
思考几个问题 1. 为什么要有规范? 2. 如何让前后端快速开发、联调?
|
思考
为什么要有规范?
- 提高开发效率:统一的规范让开发者能够快速理解和使用 API
- 降低沟通成本:减少前后端联调时的误解和返工
- 便于维护和扩展:良好的设计使系统更容易维护和升级
- 提升用户体验:一致的接口设计让客户端开发更加顺畅
如何让前后端快速开发、联调?
- 制定清晰的 API 规范文档
- 使用标准化的状态码和错误处理
- 提供完整的示例和测试环境
- 建立高效的沟通机制
一些概念
URI和URL的区别
- **URI (Uniform Resource Identifier)**:统一资源标识符,用于标识某一互联网资源
- **URL (Uniform Resource Locator)**:统一资源定位符,是 URI 的子集,不仅标识资源,还能定位资源
URI: /users/123 URL: https://api.example.com/users/123
|
http状态码的含义
状态码 |
类别 |
含义 |
2xx |
成功 |
请求成功处理 |
3xx |
重定向 |
需要后续操作 |
4xx |
客户端错误 |
请求有误 |
5xx |
服务器错误 |
服务器处理出错 |
200 OK
:请求成功
201 Created
:资源创建成功
400 Bad Request
:请求参数错误
401 Unauthorized
:未授权
403 Forbidden
:禁止访问
404 Not Found
:资源不存在
500 Internal Server Error
:服务器内部错误
Http method的含义
方法 |
用途 |
幂等性 |
安全性 |
GET |
获取资源 |
是 |
是 |
POST |
创建资源 |
否 |
否 |
PUT |
更新资源 |
是 |
否 |
PATCH |
部分更新 |
否 |
否 |
DELETE |
删除资源 |
是 |
否 |
什么是RESTful
定义
REST (Representational State Transfer) 是一种软件架构风格,RESTful API 遵循以下约束:
- 客户端-服务器架构:分离关注点
- 无状态:每个请求包含完整信息
- 可缓存:响应可以被缓存
- 统一接口:一致的接口设计
- 分层系统:系统可以分层部署
- 按需代码(可选):支持扩展功能
设计原则
URI设计
基本原则:
- 使用名词而非动词
- 使用复数形式
- 使用小写字母和连字符
- 层级结构清晰
优秀示例:
1 2 3 4 5 6 7 8 9 10 11
| ✅ 好的设计 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/getUsers GET /api/v1/UserManagement/getUser?id=123 POST /api/v1/createUser
|
版本控制:
/api/v1/users /api/v2/users
|
查询参数:
GET /api/v1/users?page=1&size=10&sort=name GET /api/v1/users?status=active&role=admin
|
状态码的使用
成功状态码:
1 2 3 4 5 6 7 8 9
| HTTP/1.1 200 OK Content-Type: application/json
{ "data": { "id": 123, "name": "张三" } }
|
1 2 3 4 5 6 7 8 9 10
| HTTP/1.1 201 Created Location: /api/v1/users/123 Content-Type: application/json
{ "data": { "id": 123, "name": "张三" } }
|
错误状态码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| HTTP/1.1 400 Bad Request Content-Type: application/json
{ "error": { "code": "INVALID_PARAMETER", "message": "参数验证失败", "details": [ { "field": "email", "reason": "邮箱格式不正确" } ] } }
|
请求和响应体的设计
统一响应格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| { "code": 200, "message": "success", "data": { "users": [ { "id": 1, "name": "张三", "email": "zhangsan@example.com" } ], "pagination": { "page": 1, "size": 10, "total": 100 } } }
|
错误响应格式:
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "code": 400, "message": "参数验证失败", "error": { "type": "VALIDATION_ERROR", "details": [ { "field": "email", "message": "邮箱格式不正确" } ] } }
|
异常处理
全局异常处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ValidationException.class) public ResponseEntity<ErrorResponse> handleValidationException(ValidationException e) { ErrorResponse error = ErrorResponse.builder() .code(400) .message("参数验证失败") .error(ErrorDetails.builder() .type("VALIDATION_ERROR") .details(e.getDetails()) .build()) .build(); return ResponseEntity.badRequest().body(error); } }
|
安全性的考虑
认证和授权:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
输入验证:
1 2 3 4 5 6
| @PostMapping("/users") public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest request) { User user = userService.create(request); return ResponseEntity.created(URI.create("/users/" + user.getId())).body(user); }
|
防止注入攻击:
文档
API 文档应该包含:
- 接口地址和方法
- 请求参数说明
- 响应格式示例
- 错误码说明
- 调用示例
推荐工具:
- Swagger/OpenAPI
- Postman
- Apiary
超媒体驱动的 API(HATEOAS)
响应中包含相关链接:
1 2 3 4 5 6 7 8 9 10 11 12
| { "data": { "id": 123, "name": "张三", "links": { "self": "/api/v1/users/123", "update": "/api/v1/users/123", "delete": "/api/v1/users/123", "orders": "/api/v1/users/123/orders" } } }
|
场景举例
查询用户列表(带条件)
GET /api/v1/users?status=active&role=admin&page=1&size=20&sort=created_at,desc
|
响应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| { "code": 200, "message": "success", "data": { "users": [ { "id": 1, "name": "张三", "email": "zhangsan@example.com", "status": "active", "role": "admin", "created_at": "2024-01-01T10:00:00Z" } ], "pagination": { "page": 1, "size": 20, "total": 150, "total_pages": 8 } } }
|
获取用户
响应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { "code": 200, "message": "success", "data": { "user": { "id": 123, "name": "张三", "email": "zhangsan@example.com", "profile": { "avatar": "https://cdn.example.com/avatar/123.jpg", "bio": "软件工程师" }, "links": { "self": "/api/v1/users/123", "orders": "/api/v1/users/123/orders" } } } }
|
删除用户
响应:
修改用户状态
1 2 3 4 5 6
| PATCH /api/v1/users/123/status Content-Type: application/json
{ "status": "inactive" }
|
响应:
1 2 3 4 5 6 7 8 9 10 11
| { "code": 200, "message": "用户状态更新成功", "data": { "user": { "id": 123, "name": "张三", "status": "inactive" } } }
|
批量修改某一个用户的属性
1 2 3 4 5 6 7 8 9 10
| PATCH /api/v1/users/123 Content-Type: application/json
{ "name": "李四", "email": "lisi@example.com", "profile": { "bio": "高级软件工程师" } }
|
响应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { "code": 200, "message": "用户信息更新成功", "data": { "user": { "id": 123, "name": "李四", "email": "lisi@example.com", "profile": { "bio": "高级软件工程师" } } } }
|
查询用户关联的机器狗
GET /api/v1/users/123/robots?page=1&size=10
|
响应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { "code": 200, "message": "success", "data": { "robots": [ { "id": 1, "name": "机器狗一号", "model": "Model-X", "status": "active" } ], "pagination": { "page": 1, "size": 10, "total": 3 } } }
|
附录
优秀案例
GitHub
GitHub 的 API 设计非常规范,是学习 RESTful API 设计的优秀范例。
Swagger
Swagger 提供了完整的 API 设计和文档解决方案,广泛应用于现代 Web 开发。
工具
- Spring HATEOAS 是 Spring 生态中实现 HATEOAS 规范的框架,可以帮助开发者轻松构建超媒体驱动的 RESTful API。
- Spring HATEOAS:点我
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @RestController @RequestMapping("/api/v1/users") public class UserController { @GetMapping public ResponseEntity<PagedResponse<User>> getUsers( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String status) { Page<User> users = userService.findUsers(page, size, status); return ResponseEntity.ok(PagedResponse.of(users)); } @GetMapping("/{id}") public ResponseEntity<UserResponse> getUser(@PathVariable Long id) { User user = userService.findById(id); return ResponseEntity.ok(UserResponse.of(user)); } @PostMapping public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest request) { User user = userService.create(request); URI location = URI.create("/api/v1/users/" + user.getId()); return ResponseEntity.created(location).body(UserResponse.of(user)); } @PutMapping("/{id}") public ResponseEntity<UserResponse> updateUser( @PathVariable Long id, @Valid @RequestBody UpdateUserRequest request) { User user = userService.update(id, request); return ResponseEntity.ok(UserResponse.of(user)); } @DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser(@PathVariable Long id) { userService.delete(id); return ResponseEntity.noContent().build(); } }
|
通过遵循这些设计原则和最佳实践,我们可以构建出优雅、易用、可维护的 RESTful API,为前后端协作提供良好的基础。