•内容协商版本控制:基于Accept头的媒体类型确定版本。
import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController publicclassUserController { @RequestMapping(value = "/api/users", version = "1.0") public ResponseEntity<List<UserV1>> getUsersV1() { // 堆代码 duidaima.com // 版本 1.0 的实现 List<UserV1> users = fetchUsersV1(); return ResponseEntity.ok(users); } @RequestMapping(value = "/api/users", version = "2.0") public ResponseEntity<List<UserV2>> getUsersV2() { // 版本 2.0 的实现 List<UserV2> users = fetchUsersV2(); return ResponseEntity.ok(users); } }在这个例子中,/api/users端点根据请求的版本(1.0或2.0)调用不同的方法。版本可以通过 URL 路径(如/api/v1/users)或请求头指定。
策略 | 描述 | 优点 | 缺点 |
URI 版本控制 | 在 URL 路径中包含版本号(如/api/v1/users) | 简单直观,易于在浏览器中测试 | 可能导致 URL 路径过长,难以维护 |
请求头版本控制 | 通过请求头指定版本(如Accept头) | 保持 URL 简洁,适合复杂版本控制 | 需要客户端支持自定义头,调试较复杂 |
查询参数版本控制 | 通过查询参数传递版本号(如/api/users?version=1) | 实现简单,URL 结构清晰 | 不够语义化,可能影响缓存策略 |
内容协商版本控制 | 基于Accept头的媒体类型确定版本 | 灵活,符合 REST 原则 | 实现复杂,客户端配置要求较高 |
import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; publicclassUserClient { // 堆代码 duidaima.com privatefinal WebClient webClient; publicUserClient(WebClient.Builder webClientBuilder) { this.webClient = webClientBuilder.baseUrl("http://example.com").build(); } public Mono<List> getUsersV1() { return webClient.get() .uri("/api/users") .accept(MediaType.valueOf("application/vnd.company.app-v1+json")) .retrieve() .bodyToMono(List.class); } }在这个例子中,客户端通过设置Accept头为application/vnd.company.app-v1+json来请求版本 1.0 的 API。这种方法确保客户端与目标 API 版本保持一致,避免版本不匹配的问题。
@FunctionalInterface public interface ApiVersionResolver { /** * 从给定的交换对象中解析版本 * @param exchange 当前交换对象 * @return 版本值,如果未找到则为 null */ @Nullable String resolveVersion(ServerWebExchange exchange); }这个接口允许开发者实现自定义的版本解析策略,从请求中提取版本信息。
public classPathApiVersionResolverimplementsApiVersionResolver { privatefinalint pathSegmentIndex; /** * 创建解析器实例 * @param pathSegmentIndex 包含 API 版本的路径段索引 */ publicPathApiVersionResolver(int pathSegmentIndex) { Assert.isTrue(pathSegmentIndex >= 0, "'pathSegmentIndex' must be >= 0"); this.pathSegmentIndex = pathSegmentIndex; } @Override public@Nullable String resolveVersion(ServerWebExchange exchange) { inti=0; for (PathContainer.Element e : exchange.getRequest().getPath().pathWithinApplication().elements()) { if (e instanceof PathContainer.PathSegment && i++ == this.pathSegmentIndex) { return e.value(); } } returnnull; } }这个类根据路径段索引从 URL 中提取版本。例如,对于路径/api/v1/users,如果设置pathSegmentIndex为 1,则会提取v1作为版本值。
public classDefaultApiVersionStrategyimplementsApiVersionStrategy { @Override public@Nullable Comparable<?> parseVersion(ServerWebExchange exchange) { } @Override public@Nullable Comparable<?> findDefaultVersion() { returnthis.defaultVersion; } @Override publicbooleanisVersionRequired() { returnthis.versionRequired; } @Override publicvoidvalidateVersion(@Nullable Comparable<?> requestVersion, ServerWebExchange exchange) throws MissingApiVersionException, InvalidApiVersionException { } }版本请求条件
public class VersionRequestCondition implements RequestCondition<VersionRequestCondition> { @Override public @Nullable VersionRequestCondition getMatchingCondition(ServerWebExchange exchange) { Comparable<?> requestVersion = this.versionStrategy.parseVersion(exchange); return this; } }结论