如何使用 spring 管理 REST API 版本控制? [英] How to manage REST API versioning with spring?

查看:42
本文介绍了如何使用 spring 管理 REST API 版本控制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在寻找如何使用 Spring 3.2.x 管理 REST API 版本,但我没有找到任何易于维护的东西.我会先解释我遇到的问题,然后是解决方案……但我确实想知道我是否在这里重新发明轮子.

I've been searching how to manage a REST API versions using Spring 3.2.x, but I haven't find anything that is easy to maintain. I'll explain first the problem I have, and then a solution... but I do wonder if I'm re-inventing the wheel here.

我想根据Accept头管理版本,例如如果一个请求有Accept头application/vnd.company.app-1.1+json,我希望spring MVC转发this 到处理此版本的方法.而且由于并非同一版本中 API 中的所有方法都发生了变化,因此我不想去我的每个控制器并为在版本之间没有更改的处理程序更改任何内容.我也不希望有逻辑来确定在控制器中使用哪个版本(使用服务定位器),因为 Spring 已经在发现要调用哪个方法.

I want to manage the version based on the Accept header, and for example if a request has the Accept header application/vnd.company.app-1.1+json, I want spring MVC to forward this to the method that handles this version. And since not all methods in an API change in the same release, I don't want to go to each of my controllers and change anything for a handler that hasn't changed between versions. I also don't want to have the logic to figure out which version to use in the controller themselves (using service locators) as Spring is already discovering which method to call.

因此将 1.0 版的 API 带到 1.8 版,其中在 1.0 版中引入了处理程序并在 v1.7 中进行了修改,我想按以下方式处理此问题.想象一下,代码在控制器内部,并且有一些代码能够从头文件中提取版本.(以下在Spring中无效)

So taken an API with versions 1.0, to 1.8 where a handler was introduced in version 1.0 and modified in v1.7, I would like handle this in the following way. Imagine that the code is inside a controller, and that there's some code that is able to extract the version from the header. (The following is invalid in Spring)

@RequestMapping(...)
@VersionRange(1.0,1.6)
@ResponseBody
public Object method1() {
   // so something
   return object;
}

@RequestMapping(...) //same Request mapping annotation
@VersionRange(1.7)
@ResponseBody
public Object method2() {
   // so something
   return object;
}

这在 spring 中是不可能的,因为这 2 个方法具有相同的 RequestMapping 注释并且 Spring 无法加载.这个想法是 VersionRange 注释可以定义一个开放或封闭的版本范围.第一种方法从 1.0 到 1.6 版本有效,而第二种方法适用于 1.7 版本(包括最新版本 1.8).我知道如果有人决定通过 99.99 版本,这种方法就会失效,但我可以接受.

This is not possible in spring as the 2 methods have the same RequestMapping annotation and Spring fails to load. The idea is that the VersionRange annotation can define an open or closed version range. The first method is valid from versions 1.0 to 1.6, while the second for version 1.7 onwards (including the latest version 1.8). I know that this approach breaks if someone decides to pass version 99.99, but that's something I'm OK to live with.

现在,由于如果不对 spring 的工作方式进行认真的返工,上述内容是不可能的,我正在考虑修改处理程序与请求匹配的方式,特别是编写我自己的 ProducesRequestCondition,并有那里的版本范围.例如

Now, since the above is not possible without a serious rework of how spring works, I was thinking of tinkering with the way handlers matched to requests, in particular to write my own ProducesRequestCondition, and have the version range in there. For example

代码:

@RequestMapping(..., produces = "application/vnd.company.app-[1.0-1.6]+json)
@ResponseBody
public Object method1() {
   // so something
   return object;
}

@RequestMapping(..., produces = "application/vnd.company.app-[1.7-]+json)
@ResponseBody
public Object method2() {
   // so something
   return object;
}

通过这种方式,我可以在注释的生产部分中定义封闭或开放的版本范围.我现在正在研究这个解决方案,但问题是我仍然需要替换一些核心 Spring MVC 类(RequestMappingInfoHandlerMappingRequestMappingHandlerMappingRequestMappingInfo),这是我不喜欢的,因为每当我决定升级到较新版本的 spring 时,这意味着额外的工作.

In this way, I can have closed or open version ranges defined in the produces part of the annotation. I'm working on this solution now, with the problem that I still had to replace some core Spring MVC classes (RequestMappingInfoHandlerMapping, RequestMappingHandlerMapping and RequestMappingInfo), which I don't like, because it means extra work whenever I decide to upgrade to a newer version of spring.

如果您有任何想法……尤其是任何以更简单、更易于维护的方式执行此操作的建议,我将不胜感激.

I would appreciate any thoughts... and especially, any suggestion to do this in a simpler, easier to maintain way.

添加赏金.要获得赏金,请回答上述问题,而不建议在控制器本身中使用此逻辑.Spring 已经有很多逻辑来选择调用哪个控制器方法,我想捎带它.

Adding a bounty. To get the bounty, please answer the question above without suggesting to have this logic in the controller themselves. Spring already has a lot of logic to select which controller method to call, and I want to piggyback on that.

我在 github 中分享了原始 POC(有一些改进):https://github.com/augusto/restVersioning

I've shared the original POC (with some improvements) in github: https://github.com/augusto/restVersioning

推荐答案

无论是否可以通过向后兼容的更改来避免版本控制(当您受到某些公司指南的约束或您的 API 客户端在错误的方式,即使他们不应该打破)抽象的需求是一个有趣的需求:

Regardless whether versioning can be avoided by doing backwards compatible changes (which might not always possible when you are bound by some corporate guidelines or your API clients are implemented in a buggy way and would break even if they should not) the abstracted requirement is an interesting one:

如何进行自定义请求映射,对请求中的标头值进行任意评估,而无需在方法主体中进行评估?

this SO answer 中所述,您实际上可以拥有相同的 @RequestMapping 并使用在运行时发生的实际路由期间使用不同的注释来区分.为此,您必须:

As described in this SO answer you actually can have the same @RequestMapping and use a different annotation to differentiate during the actual routing that happens during runtime. To do so, you will have to:

  1. 创建一个新的注解VersionRange.
  2. 实施RequestCondition.由于您将拥有诸如最佳匹配算法之类的东西,因此您必须检查使用其他 VersionRange 值注释的方法是否为当前请求提供了更好的匹配.
  3. 根据注解和请求条件实现一个VersionRangeRequestMappingHandlerMapping(如如何实现@RequestMapping 自定义属性).
  4. 在使用默认的 RequestMappingHandlerMapping 之前配置 spring 以评估您的 VersionRangeRequestMappingHandlerMapping(例如,将其顺序​​设置为 0).
  1. Create a new annotation VersionRange.
  2. Implement a RequestCondition<VersionRange>. Since you will have something like a best-match algorithm you will have to check whether methods annotated with other VersionRange values provide a better match for the current request.
  3. Implement a VersionRangeRequestMappingHandlerMapping based on the annotation and request condition (as described in the post How to implement @RequestMapping custom properties ).
  4. Configure spring to evaluate your VersionRangeRequestMappingHandlerMapping before using the default RequestMappingHandlerMapping (e.g. by setting its order to 0).

这不需要对 Spring 组件进行任何 hacky 替换,而是使用 Spring 配置和扩展机制,因此即使您更新 Spring 版本,它也应该可以工作(只要新版本支持这些机制).

This wouldn't require any hacky replacements of Spring components but uses the Spring configuration and extension mechanisms so it should work even if you update your Spring version (as long as the new version supports these mechanisms).

这篇关于如何使用 spring 管理 REST API 版本控制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆