微服務 API 版本控制

REST
Remote
0
04:18 AM · Dec 01 ,2025

1. 概述

在本教程中,我們將探討如何利用 Micronaut 框架 的能力來實現不斷演進的 REST API。

在軟件開發項目不斷演進的背景下,僅僅基於 REST API 時,在引入新功能和改進的同時保持向後兼容性是一個關鍵挑戰。 實現這一點的一個基本方面是實施一種稱為 API 版本控制的技術。

我們將探討 API 版本控制的概念,並將其置於 Micronaut 的背景下,Micronaut 是一個流行的微服務框架,用於構建高效和可擴展的應用程序。我們將深入研究 API 版本控制的重要性、在 Micronaut 中實施不同策略以及最佳實踐,以確保平穩的版本過渡。

2. 重要的API版本管理

API版本管理是指管理和演進應用程序編程接口(API),以便客户端繼續使用現有版本,並在準備好時採用新版本。這對於幾個原因至關重要。

2.1. 保持兼容性

隨着我們應用程序的演進,我們可能需要更改我們的API以引入新功能、修復錯誤或提高性能。但是,也必須確保這些更改不會中斷現有客户端。API版本管理使我們能夠引入更改同時保持與先前版本的兼容性

2.2. 允許逐步採用

我們API的客户端可能具有不同的新版本採用時間表。因此,提供我們API的多個版本使客户端能夠以合理的採用時間更新其代碼,從而降低其應用程序中斷的風險。

2.3. 促進協作

它還促進了開發團隊之間的協作。當不同的團隊在其他系統組件上工作,或第三方開發人員與我們的API集成時,版本管理使每個團隊都能在其他地方進行更改時擁有穩定的接口。

3. API Versioning Strategies in Micronaut

Micronaut offers different strategies for implementing API versioning. We aren’t going to discuss which one is the best one as it pretty much depends on the use case and the reality of the project. Nonetheless, we can discuss the specifics of each one of them.

3.1. URI Versioning

In the URI versioning, the version of the API is defined in the URI. This approach makes it clear which version of the API the client is consuming. Although the URL may not be as user-friendly as it can be, it clarifies to the client which version it uses.

@Controller("/v1/sheep/count")
public class SheepCountControllerV1 {

    @Get(
        uri = "{?max}",
        consumes = {"application/json"},
        produces = {"application/json"}
    )
    Flowable<String> countV1(@Nullable Integer max) {
        // implementation
    }
}

Although it may not be practical, our clients are sure about the version used, which means transparency. From the development side, it’s easy to implement any business rules specific to a particular version, meaning a good level of isolation. However, one could argue it’s intrusive as the URI may change frequently. It may require hard coding from the client side and adds extra context not precisely specific to the resource.

3.2. Header Versioning

Another option to implement API versioning is to leverage the header to route the request to the right controller. Here is an example:

@Controller("/dog")
public class DogCountController {

    @Get(value = "/count", produces = {"application/json"})
    @Version("1")
    public Flowable<String> countV1(@QueryValue("max") @Nullable Integer max) {
        // logic
    }

    @Get(value = "/count", produces = {"application/json"})
    @Version("2")
    public Flowable<String> countV2(@QueryValue("max") @NonNull Integer max) {
        // logic
    }
}

By simply using the @Version annotation, Microunat can redirect the request to the proper handler based on the header’s value. However, we still need to change some configurations, as we see next:

micronaut:
  router:
    versioning:
      enabled: true
      default-version: 2
      header:
        enabled: true
        names:
          - 'X-API-VERSION'

Now we just enabled versioning via Micronaut, defining version 2 as the default one in case no version is specified. The strategy used will be header-based, and the header X-API-VERSIONwill be used to determine the version. Actually, this is the default header Micronaut looks at, so in this case, there wouldn’t be a need to define it, but in the case we want to use another header, we could specify it like this.

Using headers, the URI remains clear and concise, we can preserve backward compatibility, the URI is purely resource-based, and it allows for more flexibility in the evolution of the API. However, it’s less intuitive and visible. The client has to be aware of the version he wants to use, and it’s a bit more error-prone. There is another similar strategy that consists of the use of MineTypes for this.

3.3. Parameter Versioning

This strategy leverages query parameters in the URI to do the routing. In terms of implementation in Mircronaut, it’s exactly like the previous strategy. We just need to add the @Version in our controllers. However, we need to change some properties:

micronaut:
  router:
    versioning:
      enabled: true
      default-version: 2
      parameter:
        enabled: true
        names: 'v,api-version'

With this, the client only needs to pass either v orapi-version as query parameters in each request, and Micronat will take care of the routing for us.

When using this strategy once again, the URI will have no resource-related information, although less than having to change the URI itself. Besides that, the versioning is less explicit and more error-prone as well. This is not RESTful, and documentation is needed to avoid confusion. However, we can also appreciate the simplicity of the solution.

3.4. Custom Versioning

Micronaut also offers a custom way to implement API versioning where we can implement a versioning route resolver and show Micronaut which version to use. The implementation is simple, and we only need to implement an interface, like in the following example:

@Singleton
@Requires(property = "my.router.versioning.enabled", value = "true")
public class CustomVersionResolver implements RequestVersionResolver {

    @Inject
    @Value("${micronaut.router.versioning.default-version}")
    private String defaultVersion;

    @Override
    public Optional<String> resolve(HttpRequest<?> request) {
        var apiKey = Optional.ofNullable(request.getHeaders().get("api-key"));

        if (apiKey.isPresent() && !apiKey.get().isEmpty()) {
            return Optional.of(Integer.parseInt(apiKey.get())  % 2 == 0 ? "2" : "1");
        }

        return Optional.of(defaultVersion);
    }

}

Here, we can see how we can leverage any information in the request to implement a routing strategy, and Micronaut does the rest. This is powerful, but we need to be cautious because this may lead to poor and less intuitive forms of implementing versioning.

4. 結論

在本文中,我們看到了如何使用 Micronaut 實現 API 版本控制。此外,我們還討論了這種技術中存在的各種策略及其細微之處。

同時,我們也清楚地認識到選擇合適的策略需要權衡 URI 清潔度、版本明確性、易用性、向後兼容性、 RESTful 規範以及消費 API 的客户端特定需求。 最佳方法取決於我們項目的獨特要求和約束條件。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.