RESTful 与内容协商、统一响应体

结论先行:REST 把一切抽象为资源,用 HTTP 方法 + 状态码表达语义:GET 查、POST 增、PUT 全量替换、PATCH 局部更新、DELETE 删。Spring MVC 里 @RestController + @GetMapping/@PostMapping 等组合注解即声明式 REST。内容协商指同一资源按客户端偏好返回不同表示(Accept 头 / URL 后缀 .json/.xml / format 参数),由 ContentNegotiationManager 决策、HttpMessageConverter 负责序列化。接口工程普遍用 统一响应体 Result<T>(code/msg/data)+ 全局异常约束契约。

一、RESTful 关键点

  • 资源用名词复数路径(/orders/{id}),动作交给 HTTP 动词,不出现 /getOrder、/deleteOrder 式 RPC 风格;
  • 用状态码表达结果:200/201/204、400/401/403/404、500;幂等语义:GET/PUT/DELETE 幂等、POST 不保证;
  • @PathVariable 取路径参数、@RequestBody 传资源 JSON。

二、内容协商机制

  • 决策顺序:Accept 头优先,其次(若开启)URL 后缀,再次 format 请求参数;
  • 写回阶段挑选 HttpMessageConverter:JSON 用 MappingJackson2HttpMessageConverter;要输出 XML 需引入 jackson-dataformat-xml 等并注册;
  • 服务端一般只暴露 JSON 即可,不必开放 format 参数(避免被滥用切换格式);
  • 消费端对应:RestTemplate/Feign 默认 Accept: application/json。
public record Result<T>(int code, String msg, T data) {
    public static <T> Result<T> ok(T data) { return new Result<>(200, "ok", data); }
    public static <T> Result<T> fail(int code, String msg) { return new Result<>(code, msg, null); }
}

@RestController
@RequestMapping("/orders")
public class OrderController {
    @GetMapping("/{id}")                       // GET 查询资源
    public Result<Order> get(@PathVariable Long id) { return Result.ok(orderService.get(id)); }

    @PostMapping                               // POST 创建资源
    public Result<Order> create(@RequestBody @Valid OrderDTO dto) { return Result.ok(orderService.create(dto)); }
}

三、统一响应体注意

  • 包装后要配合全局异常处理器保证"所有路径都返回 Result"(见 23);
  • 陷阱:Controller 直接返回 String 时会被 StringHttpMessageConverter 处理、不会被 Result 包装,需要统一包装时让方法返回 Object/Result 或单独处理;
  • 与前端/OpenAPI 契约保持一致,code=0/200 的业务码约定要写进文档。

常见追问 / 记忆点

  • 追问 1:PUT 与 PATCH 区别?答:PUT 全量替换资源(缺字段可能置空),PATCH 只更新传入字段。
  • 追问 2:内容协商由谁实现?答:ContentNegotiationManager 决定媒体类型,HttpMessageConverter 决定能否读写该类型,两者协作。
  • 追问 3:REST 与 RPC 怎么选?答:面向资源、多端、HTTP 语义丰富用 REST;内部强类型、性能敏感、方法调用语义用 RPC(Dubbo/gRPC)。
  • 记忆点:"资源 + 动词 + 状态码;Accept 说我要什么,MessageConverter 负责变;响应统一包 Result"。
笔记加载中…