闽公网安备 35020302035485号
•内容协商版本控制:基于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;
}
}
结论