WebService 兩大陣營:SOAP vs REST
| 維度 | SOAP (Simple Object Access Protocol) | REST (Representational State Transfer) |
| 通信協議 | HTTP/HTTPS (也可 SMTP, JMS...) | HTTP/HTTPS (充分利用 HTTP 語義) |
| 數據格式 | 強制 XML (SOAP Envelope) | 靈活:JSON (主流)、XML、Plain Text、Protobuf... |
| 接口描述 | WSDL (XML 文檔,強契約) | 無強制標準:OpenAPI/Swagger (事實標準)、RAML、Postman Collection |
| 狀態管理 | 通常有狀態 (WS-* 規範支持) | 無狀態 (Stateless),每請求自包含上下文 |
| HTTP 方法映射 | 全部 POST (SOAP Action 在 Header/Body) | 語義化映射:GET/POST/PUT/PATCH/DELETE |
| 緩存支持 | 困難 (POST 不緩存) | 原生支持 (GET/HEAD 配合 Cache-Control/ETag) |
| 複雜度 | 高 (重量級、學習曲線陡) | 低 (輕量級、易上手、人類可讀) |
| 適用場景 | 企業級集成、強事務/安全需求 (銀行、電信)、異構系統強契約 | Web/API 開發、移動端、微服務、前後端分離、公開 API |
REST 核心架構約束 (Fielding 博士論文)
- 客戶端-服務器:關注點分離,獨立演進。
- 無狀態:服務端不保存客戶端會話狀態,認證信息 (Token) 隨請求發送。
- 可緩存:響應隱式/顯式標記可緩存性,提升性能。
- 統一接口:資源標識 (URI)、資源表述 (JSON/XML)、自描述消息 (HTTP Header)、超媒體驅動狀態遷移 (HATEOAS)。
- 分層系統:代理、網關、負載均衡對客戶端透明。
- 按需代碼 (可選):下載並執行腳本/Applet (極少用)。
Java 實現 REST 技術棧選型 (主流)
| 框架/規範 | 定位 | 特點 | 適用場景 |
| JAX-RS (JSR 370/339/311) | Java EE 標準規範 | 註解驅動 (@Path, @GET, @Produces),可換實現 | 標準化項目、避免廠商鎖定 |
| Jersey | JAX-RS 參考實現 | 功能全、擴展性強、Spring 整合好 | 傳統 Java EE / Spring Boot 非 WebFlux 項目 |
| RESTEasy | JBoss/Red Hat 實現 | 性能優、集成 WildFly、客戶端框架強 | JBoss 生態、高性能需求 |
| Spring MVC / Spring Boot Web | Spring 生態事實標準 | @RestController、@RequestMapping、強大生態、文檔生成 (SpringDoc/OpenAPI) | 國內 90%+ 項目首選 |
| Spring WebFlux | 響應式/非阻塞 | Reactor、Netty、背壓、函數式路由 | 高併發、長連接、流式處理、微服務網關 |
| Vert.x | 輕量級、事件驅動、Polyglot | 極高吞吐、無阻塞、Kotlin/Java/JS 通用 | 高性能網關、實時系統、Sidecar |
Spring Boot 實現 REST 微型示例
// 1. 依賴: spring-boot-starter-web
// 2. 實體
@Data @AllArgsConstructor @NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
}
// 3. Controller (REST 風格)
@RestController
@RequestMapping("/api/users")
public class UserController {
private final Map<Long, User> repo = new ConcurrentHashMap<>();
private final AtomicLong idGen = new AtomicLong();
@GetMapping
public List<User> list() {
return new ArrayList<>(repo.values());
}
@GetMapping("/{id}")
public ResponseEntity<User> get(@PathVariable Long id) {
return repo.containsKey(id) ?
ResponseEntity.ok(repo.get(id)) :
ResponseEntity.notFound().build();
}
@PostMapping
public ResponseEntity<User> create(@RequestBody User user) {
Long id = idGen.incrementAndGet();
user.setId(id);
repo.put(id, user);
// 返回 201 Created + Location Header
return ResponseEntity.created(URI.create("/api/users/" + id)).body(user);
}
@PutMapping("/{id}")
public ResponseEntity<User> update(@PathVariable Long id, @RequestBody User user) {
if (!repo.containsKey(id)) return ResponseEntity.notFound().build();
user.setId(id);
repo.put(id, user);
return ResponseEntity.ok(user);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
if (!repo.containsKey(id)) return ResponseEntity.notFound().build();
repo.remove(id);
return ResponseEntity.noContent().build(); // 204 No Content
}
}
REST 設計最佳實踐清單
- 資源導向 URI: 名詞複數
/users, /orders,非動詞 /getUser。
- HTTP 方法語義對齊: GET(查)、POST(增)、PUT(全量改)、PATCH(部分改)、DELETE(刪)。
- 狀態碼精準: 200/201/204/400/401/403/404/409/500... 不要通用 200/500。
- 版本控制: URI
/v1/users 或 Header Accept: application/vnd.myapp.v1+json。
- 分頁/過濾/排序: Query Param
?page=1&size=20&sort=name,desc。
- 錯誤響應統一格式:
{"timestamp":..., "status":404, "error":"Not Found", "message":"...", "path":...} (RFC 7807 ProblemDetails)。
- 安全: HTTPS、JWT/OAuth2、速率限制、輸入驗證 (Bean Validation)。
- 文檔自動生成:
springdoc-openapi-starter-webmvc-ui 產出 Swagger UI。
結語: REST 不是標準而是架構風格,Java 生態以 Spring Boot (MVC/WebFlux) 實現最成熟高效。掌握 HTTP 語義、狀態碼、資源建模、版本與錯誤規範,比熟悉具體框架 API 更重要。