web-第9次课后作业

本项目 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 标准,达到生产环境可用的设计水平。