> ## Documentation Index
> Fetch the complete documentation index at: https://blog.pig4cloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Spring 7.0 新特性太香了，让 API 变更更加丝滑

API 版本控制是现代 Web 开发中的关键实践，允许开发者在不破坏现有客户端的情况下管理 API 的变更。Spring Framework 7.0 引入了对 API 版本控制的原生支持，为服务器端和客户端应用程序提供了强大的工具，以高效处理版本特定的路由和请求。

## API 版本控制的重要性

API 版本控制允许开发者在 API 演进过程中引入新功能、修复错误或更改数据结构，同时确保向后兼容性。常见的版本控制策略包括：

* **URI 版本控制**：在 URL 路径中包含版本号，例如 `/api/v1/users`。
* **请求头版本控制**：通过请求头（如 `Accept` 头）指定版本，例如 `application/vnd.company.app-v1+json`。
* **查询参数版本控制**：通过查询参数传递版本号，例如 `/api/users?version=1`。
* **内容协商版本控制**：基于 `Accept` 头的媒体类型确定版本。

## Spring 7.0 API 版本控制

<img src="https://minio.pigx.vip/oss/202504/1745183810.png" alt="1745183810" />

Spring Framework 7.0 为服务器端 Web 应用程序引入了 API 版本控制支持，允许通过 `@RequestMapping` 注解中的版本范围将请求映射到不同的控制器方法。这种方法简化了多版本 API 的管理，使开发者能够在一个应用程序中处理多个 API 版本。

### 实现方式

开发者可以在 `@RequestMapping` 注解中指定版本范围，Spring 会根据请求中的版本信息将请求路由到相应的控制器方法。版本可以从以下来源解析：

* **请求 URL 路径**：例如，`/api/v1/users` 或 `/api/v2/users`。
* **请求头值**：例如，`Accept: application/vnd.company.app-v1+json`。
* **其他自定义来源**：开发者可以配置额外的版本解析方式，以满足特定需求。

以下是一个示例，展示如何在控制器中实现不同版本的 API 端点：

```java theme={null}
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @RequestMapping(value = "/api/users", version = "1.0")
    public ResponseEntity<List<UserV1>> getUsersV1() {
        // 版本 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`）或请求头指定。

### 版本解析的灵活性

Spring Framework 7.0 的版本解析机制支持多种策略。例如，开发者可以配置应用程序以从自定义请求头或查询参数中提取版本信息。这种灵活性使得 Spring Framework 7.0 能够适应不同的版本控制需求，从简单的 URI 版本控制到复杂的基于内容的协商。

| 策略       | 描述                                    | 优点                 | 缺点                 |
| -------- | ------------------------------------- | ------------------ | ------------------ |
| URI 版本控制 | 在 URL 路径中包含版本号（如 `/api/v1/users`）     | 简单直观，易于在浏览器中测试     | 可能导致 URL 路径过长，难以维护 |
| 请求头版本控制  | 通过请求头指定版本（如 `Accept` 头）               | 保持 URL 简洁，适合复杂版本控制 | 需要客户端支持自定义头，调试较复杂  |
| 查询参数版本控制 | 通过查询参数传递版本号（如 `/api/users?version=1`） | 实现简单，URL 结构清晰      | 不够语义化，可能影响缓存策略     |
| 内容协商版本控制 | 基于 `Accept` 头的媒体类型确定版本                | 灵活，符合 REST 原则      | 实现复杂，客户端配置要求较高     |

Spring Framework 7.0 的灵活性允许开发者根据项目需求选择最合适的策略。例如，简单的应用程序可能更适合 URI 版本控制，而需要复杂版本管理的企业应用程序可能更倾向于请求头或内容协商版本控制。

## 客户端 API 版本控制

除了服务器端支持，Spring Framework 7.0 还增强了客户端的功能。使用 Spring 的 `WebClient` 或其他客户端工具时，开发者可以在发送请求时指定所需的 API 版本。这确保客户端与正确的 API 版本交互，特别是在多个 API 版本同时部署的情况下。

### 客户端示例

以下是一个使用 `WebClient` 指定 API 版本的示例：

```java theme={null}
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class UserClient {

    private final WebClient webClient;

    public UserClient(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 版本保持一致，避免版本不匹配的问题。

## API 版本控制源码解析

Spring Framework 7.0 的 API 版本控制功能主要由几个核心类实现，下面我们对其中几个关键类进行深入分析。

### ApiVersionResolver 接口

`ApiVersionResolver` 是一个函数式接口，定义了从请求中解析版本的契约：

```java theme={null}
@FunctionalInterface
public interface ApiVersionResolver {

    /**
     * 从给定的交换对象中解析版本
     * @param exchange 当前交换对象
     * @return 版本值，如果未找到则为 null
     */
    @Nullable String resolveVersion(ServerWebExchange exchange);
}
```

这个接口允许开发者实现自定义的版本解析策略，从请求中提取版本信息。

### PathApiVersionResolver 实现

`PathApiVersionResolver` 是 `ApiVersionResolver` 的一个实现，用于从 URL 路径中提取版本信息：

```java theme={null}
public class PathApiVersionResolver implements ApiVersionResolver {

    private final int pathSegmentIndex;

    /**
     * 创建解析器实例
     * @param pathSegmentIndex 包含 API 版本的路径段索引
     */
    public PathApiVersionResolver(int pathSegmentIndex) {
        Assert.isTrue(pathSegmentIndex >= 0, "'pathSegmentIndex' must be >= 0");
        this.pathSegmentIndex = pathSegmentIndex;
    }

    @Override
    public @Nullable String resolveVersion(ServerWebExchange exchange) {
        int i = 0;
        for (PathContainer.Element e : exchange.getRequest().getPath().pathWithinApplication().elements()) {
            if (e instanceof PathContainer.PathSegment && i++ == this.pathSegmentIndex) {
                return e.value();
            }
        }
        return null;
    }
}
```

这个类根据路径段索引从 URL 中提取版本。例如，对于路径 `/api/v1/users`，如果设置 `pathSegmentIndex` 为 1，则会提取 `v1` 作为版本值。

### DefaultApiVersionStrategy 类

`DefaultApiVersionStrategy` 是 API 版本策略的默认实现，提供了版本解析、比较和验证的核心功能：

```java theme={null}
public class DefaultApiVersionStrategy implements ApiVersionStrategy {

    @Override
    public @Nullable Comparable<?> parseVersion(ServerWebExchange exchange) {

    }

    @Override
    public @Nullable Comparable<?> findDefaultVersion() {
        return this.defaultVersion;
    }

    @Override
    public boolean isVersionRequired() {
        return this.versionRequired;
    }

    @Override
    public void validateVersion(@Nullable Comparable<?> requestVersion, ServerWebExchange exchange)
            throws MissingApiVersionException, InvalidApiVersionException {
    }
}
```

### 版本请求条件

版本控制功能的核心是 `VersionRequestCondition` 类，它作为请求映射的条件之一，用于根据请求中的版本将请求路由到合适的处理方法：

```java theme={null}
public class VersionRequestCondition implements RequestCondition<VersionRequestCondition> {
    @Override
    public @Nullable VersionRequestCondition getMatchingCondition(ServerWebExchange exchange) {
        Comparable<?> requestVersion = this.versionStrategy.parseVersion(exchange);
      
        return this;
    }
}
```

## 结论

Spring Framework 7.0 的 API 版本控制实现提供了一套灵活、可扩展的机制，允许开发者以一种标准化的方式管理 API 版本。通过清晰的接口设计和可配置的策略，Spring Framework 使得在 WebFlux 应用程序中实现 API 版本控制变得简单而强大。

核心类如 `ApiVersionResolver`、`ApiVersionStrategy` 和 `VersionRequestCondition` 协同工作，提供了从请求解析版本、验证版本并路由到适当处理方法的完整流程。这种设计不仅支持常见的版本控制策略，还允许开发者实现自定义的版本解析和验证逻辑，以满足特定的业务需求。
