本项目 API 遵循 RESTful 架构风格,达到 Richardson 成熟度模型 Level 2(行业标准级别)。以下逐条列出证据。
一、Richardson 成熟度模型定位
| Level | 要求 | 本项目 |
|---|---|---|
| 0 | HTTP 仅作传输,单一 URL + POST 处理一切 | 已超越 |
| 1 | 资源导向:URL 使用名词标识资源 | 满足 |
| 2 | HTTP 动词:用 GET/POST/PUT/DELETE 表达操作语义 | 满足 |
| 3 | HATEOAS:响应中包含超媒体链接引导客户端 | 未实现 |
行业共识:Level 2 是绝大多数互联网公司(Google、GitHub、Stripe)的实际标准。Level 3 仅在超媒体驱动的特殊场景中使用,Spring HATEOAS 成本高、收益低。
二、原则一:URL 使用名词,不用动词
规范:URL 应标识"资源是什么",不应描述"对资源做什么"。
证据 --- controller/UserController.java 第 14 行:
java
@RequestMapping("/api/users") // ✓ 名词 "users",非 "getUsers" 或 "doQuery"
对比:
| 反面案例(非 RESTful) | 本项目(RESTful) |
|---|---|
/api/getUsers |
GET /api/users |
/api/addUser |
POST /api/users |
/api/updateUser |
PUT /api/users/{id} |
/api/deleteUser?id=1 |
DELETE /api/users/{id} |
三、原则二:HTTP 方法表达操作语义
规范:用 HTTP 方法区分操作,而非在 URL 中体现。
证据 --- controller/UserController.java 第 23--68 行:
java
@GetMapping // GET → 查询(安全、幂等)
@GetMapping("/{id}") // GET → 查询单个(安全、幂等)
@PostMapping // POST → 新增(非幂等)
@PutMapping("/{id}") // PUT → 全量更新(幂等)
@DeleteMapping("/{id}") // DELETE → 删除(幂等)
幂等性验证
| 方法 | 连续调用 2 次的结果 | 幂等 | 说明 |
|---|---|---|---|
GET /api/users |
返回相同数据 | 是 | 只读 |
POST /api/users |
新增 2 条记录(不同 ID) | 否 | 每次创建新资源 |
PUT /api/users/1 |
第 2 次写入相同字段,结果不变 | 是 | 全量替换 |
DELETE /api/users/1 |
第 2 次返回 404 | 是 | 资源已不存在 |
POST 非幂等是正确的------新增操作每次产生新资源,这是 RESTful 规范明确允许的。
四、原则三:HTTP 状态码传达结果
规范:用 HTTP 状态码(而非 body 中的自定义字段)告诉客户端操作结果。
4.1 代码证据
controller/UserController.java:
java
return ResponseEntity.ok(...) // 200 OK --- 第 31、40、57、67 行
return ResponseEntity.status(201).body(...) // 201 Created --- 第 46 行
return ResponseEntity.status(404).body(...) // 404 Not Found --- 第 38、53、64 行
4.2 状态码使用对照
| 场景 | HTTP 状态码 | 代码行号 |
|---|---|---|
| 查询全部成功 | 200 OK |
第 31 行 |
| 按 ID 查询成功 | 200 OK |
第 40 行 |
| 查询的 ID 不存在 | 404 Not Found |
第 38 行 |
| 新增成功 | 201 Created |
第 46 行 |
| 更新成功 | 200 OK |
第 57 行 |
| 更新的 ID 不存在 | 404 Not Found |
第 53 行 |
| 删除成功 | 200 OK |
第 67 行 |
| 删除的 ID 不存在 | 404 Not Found |
第 64 行 |
4.3 验证方法
浏览器打开 F12 → Network 标签,操作后可见:
200绿色 --- 请求成功201绿色 --- 资源已创建404红色 --- 资源不存在
状态码直接向浏览器、网关、监控系统传达结果,无需解析 body。
五、原则四:无状态(Stateless)
规范:服务端不保存客户端会话状态,每个请求独立且自包含。
证据:
UserController中无HttpSession、无@SessionAttributes、无 Cookie 校验- 每个请求仅依赖 URL 参数和请求体,不依赖"上一个请求做了什么"
- 前端通过 Axios 每次独立发送请求,不维持服务端 session
java
// UserController 中没有以下任何代码:
// HttpSession session;
// @SessionAttributes
// request.getSession()
// Cookie cookie;
六、原则五:资源层级化
规范:资源的嵌套关系在 URL 中体现。
证据:
/api/users ← 用户集合(collection resource)
/api/users/1 ← 集合中的单个资源(item resource)
单个资源的 ID 使用路径参数 @PathVariable,而非查询参数 ?id=1:
java
@GetMapping("/{id}") // ✓ /api/users/1
public ResponseEntity<...> getById(@PathVariable Integer id) // ✓ 路径参数
对比:
| 非 RESTful | RESTful(本项目) |
|---|---|
/api/users?id=1(查询参数) |
/api/users/1(路径参数) |
/api/users/get?id=1(动词+查询参数) |
/api/users/1(名词+路径参数) |
七、原则六:统一响应格式
规范:成功和失败使用一致的 JSON 数据结构。
证据 --- 所有接口返回 Map.of("message", ..., "data", ...):
json
// 成功 --- 200 OK
{ "message": "success", "data": [ { "id": 1, "name": "白眉鹰王", ... } ] }
// 成功 --- 201 Created
{ "message": "新增成功", "data": { "id": 7, "name": "张无忌", ... } }
// 失败 --- 404 Not Found
{ "message": "用户不存在", "data": null }
@RestController 的作用 :自动将返回的 Java 对象序列化为 JSON(通过 Jackson),无需手动调用 JSON.stringify。
八、合规总结
| RESTful 原则 | 证据来源 | 结论 |
|---|---|---|
| URL 用名词 | @RequestMapping("/api/users") |
合规 |
| HTTP 方法语义 | @GetMapping / @PostMapping / @PutMapping / @DeleteMapping |
合规 |
| HTTP 状态码 | ResponseEntity.ok() / status(201) / status(404) |
合规 |
| 无状态 | 无 Session / 无 Cookie 依赖 | 合规 |
| 资源层级 | /api/users/{id} @PathVariable |
合规 |
| 统一 JSON 格式 | @RestController + Map.of(...) |
合规 |
| 幂等性 | GET/PUT/DELETE 幂等,POST 非幂等 | 合规 |
| HATEOAS | 未实现 | 超出 Level 2,行业可接受 |
结论:本项目 API 完全符合 RESTful Level 2 标准,达到生产环境可用的设计水平。