API版本控制
API版本控制
URL路径版本
最直观的版本控制方式,通过URL路径区分不同版本。
@RestController
@RequestMapping("/api/v1/users")
public class UserV1Controller {
@GetMapping("/{id}")
public UserV1DTO getUser(@PathVariable Long id) {
return userService.findByIdV1(id);
}
}
@RestController
@RequestMapping("/api/v2/users")
public class UserV2Controller {
@GetMapping("/{id}")
public UserV2DTO getUser(@PathVariable Long id) {
return userService.findByIdV2(id);
}
}
Header版本
通过自定义Header传递版本信息,保持URL不变。
@RestController
@RequestMapping("/api/users")
public class UserVersionedController {
@GetMapping(value = "/{id}", headers = "X-API-Version=1")
public UserV1DTO getUserV1(@PathVariable Long id) {
return userService.findByIdV1(id);
}
@GetMapping(value = "/{id}", headers = "X-API-Version=2")
public UserV2DTO getUserV2(@PathVariable Long id) {
return userService.findByIdV2(id);
}
}
Query参数版本
通过查询参数指定API版本,灵活但不够优雅。
@RestController
@RequestMapping("/api/users")
public class UserQueryVersionController {
@GetMapping("/{id}")
public ResponseEntity<?> getUser(
@PathVariable Long id,
@RequestParam(name = "version", defaultValue = "1") int version) {
return switch (version) {
case 2 -> ResponseEntity.ok(userService.findByIdV2(id));
default -> ResponseEntity.ok(userService.findByIdV1(id));
};
}
}
版本协商
客户端和服务端协商确定使用的API版本。
@Component
public class ApiVersionNegotiator {
public int negotiateVersion(HttpServletRequest request) {
// 1. 检查Header
String headerVersion = request.getHeader("X-API-Version");
if (headerVersion != null) {
return Integer.parseInt(headerVersion);
}
// 2. 检查Accept头
String accept = request.getHeader("Accept");
if (accept != null && accept.contains("application/vnd.myapp.v")) {
int start = accept.indexOf("v") + 1;
int end = accept.indexOf("+", start);
return Integer.parseInt(accept.substring(start, end));
}
// 3. 默认版本
return 1;
}
}
版本演进策略
渐进式废弃旧版本,引导客户端升级。
@Component
public class VersionDeprecationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws Exception {
String path = request.getRequestURI();
if (path.startsWith("/api/v1/")) {
response.setHeader("Deprecation", "true");
response.setHeader("Sunset", "Sat, 01 Jul 2025 00:00:00 GMT");
response.setHeader("Link", "</api/v2>; rel=\"successor-version\"");
}
filterChain.doFilter(request, response);
}
}