首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于JAX实现的轻松REST资源版本控制?

基于JAX实现的轻松REST资源版本控制?
EN

Stack Overflow用户
提问于 2011-02-07 16:58:52
回答 5查看 11.5K关注 0票数 11

REST资源版本控制的最佳实践是将版本信息放入HTTP请求的Accept/Content报头中,保持URI不变。

下面是用于检索系统信息的REST请求/响应示例:

代码语言:javascript
复制
==>
GET /api/system-info HTTP/1.1
Accept: application/vnd.COMPANY.systeminfo-v1+json

<==
HTTP/1.1 200 OK
Content-Type: application/vnd.COMPANY.systeminfo-v1+json
{
  “session-count”: 19
}

注意版本是以MIME类型指定的。

以下是版本2的另一个请求/响应:

代码语言:javascript
复制
==>
GET /api/system-info HTTP/1.1
Accept: application/vnd.COMPANY.systeminfo-v2+json

<==
HTTP/1.1 200 OK
Content-Type: application/vnd.COMPANY.systeminfo-v2+json
{
  “uptime”: 234564300,
  “session-count”: 19
}

有关更多解释和示例,请参见http://barelyenough.org/blog/tag/rest-versioning/

在基于Java的JAX-RS实现(如泽西或Apache )中能够轻松地实现这种方法吗?

目标是拥有几个具有相同@路径值的@Resource类,但根据MIME类型中指定的实际版本为请求提供服务?

我已经研究了JAX和泽西岛在部分克隆,发现没有支持这一点。泽西岛没有机会用相同的路径注册两个资源。WebApplicationImpl类的替换需要实现以支持这一点。

你能提点建议吗?

注意:同一资源的多个版本需要同时使用,这是必需的。新版本可能会带来不兼容的更改.

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-02-07 22:00:50

JAX通过Accept头向带有@Produces注解的方法发送。因此,如果您希望JAX执行您的调度,则需要利用此机制。如果没有任何额外的工作,您就必须为希望支持的每一种媒体类型创建一个方法(和提供程序)。

没有什么可以阻止您有几种基于媒体类型的方法,这些方法都调用一个通用方法来完成这项工作,但是每次添加新的媒体类型时,您都必须更新这些方法并添加代码。

其中一个想法是添加一个过滤器,专门为分派而“正常化”您的接受头。这也许是,拿着你的:

代码语言:javascript
复制
Accept: application/vnd.COMPANY.systeminfo-v1+json

并将其转化为:

代码语言:javascript
复制
Accept: application/vnd.COMPANY.systeminfo+json

同时,您提取版本信息供以后使用(可能在请求中,或者其他一些临时机制中)。

然后,JAX将发送给处理"application/vnd.COMPANY.systeminfo+json“的单一方法。

然后,该方法获取“带外”版本控制信息来处理处理中的细节(例如选择要通过OSGi加载的适当类)。

接下来,创建一个具有适当MessageBodyWriter的提供程序。提供程序将由JAX为application/vnd.COMPANY.systeminfo+json媒体类型选择。这将取决于您的MBW来确定实际的媒体类型(再次基于该版本信息),并创建正确的输出格式(同样,可能会分派给正确的OSGi加载类)。

我不知道MBW是否可以覆盖内容类型标头。如果没有,则可以委托先前的筛选器在退出时为您重写该部分。

这有点令人费解,但如果您想利用JAX调度,而不是为您的媒体类型的每个版本创建方法,那么这是一个可能的途径。

编辑以回应评论:

实际上,您希望JAX根据路径和接受类型向适当的类分派。JAX不太可能做到这一点,因为这是一个边缘的情况。我没有看过任何JAX实现,但是您可以通过在基础设施级别上调整其中一个来实现您想做的事情。

另一个更具侵入性的选项可能是从Apache世界中使用一个古老的技巧,并简单地创建一个基于Accept标头重写路径的筛选器。

因此,当系统获得:

代码语言:javascript
复制
GET /resource
Accept: application/vnd.COMPANY.systeminfo-v1+json

你把它重写为:

代码语言:javascript
复制
GET /resource-v1
Accept: application/vnd.COMPANY.systeminfo-v1+json

然后,在JAX-RS类中:

代码语言:javascript
复制
@Path("resource-v1")
@Produces("application/vnd.COMPANY.systeminfo-v1+json")
public class ResourceV1 {
    ...
}

因此,您的客户端获得了正确的视图,但是您的类由JAX正确地分派。唯一的其他问题是,如果您的类查看,它们将看到修改后的路径,而不是原始路径(但如果您愿意,过滤器可以将其作为引用填充在请求中)。

这并不理想,但它(大部分)是免费的。

是一个现有的过滤器,它可以完成您想做的事情,如果不这样做的话,它可能会激励您自己去做。

票数 6
EN

Stack Overflow用户

发布于 2018-06-18 20:54:12

对于泽西的当前版本,我建议使用两个不同的API方法和两个不同的返回值来实现,这些方法和返回值将自动序列化为适用的MIME类型。一旦收到对API不同版本的请求,就可以在下面使用通用代码。

示例:

代码语言:javascript
复制
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public VersionOneDTO get(@PathParam("id") final String id) {

    return new VersionOneDTO( ... );

}

@GET
@Path("/{id}")
@Produces("application/vnd.COMPANY.systeminfo-v2+json;qs=0.9")
public VersionTwoDTO get_v2(@PathParam("id") final String id) {

    return new VersionTwoDTO( ... );

}

如果方法get(...)get_v2(...)使用公共逻辑,那么如果它与API相关(例如会话或JWT处理),或者放在您通过继承或依赖项注入访问的服务层的公共方法中,我建议将它们放在公共方法中。通过有两个具有不同返回类型的不同方法,您可以确保返回的结构对于不同版本的API是正确的。

请注意,一些老客户端可能根本不指定接受标头。这意味着它们将接受任何内容类型,从而接受API的任何版本。在实践中,这往往不是事实。因此,您应该使用MIME类型的qs扩展为新版本的API指定权重,如上面示例中的@Produces注释所示。

如果您正在使用restAssured进行测试,它将如下所示:

代码语言:javascript
复制
import static com.jayway.restassured.RestAssured.get;
import static com.jayway.restassured.RestAssured.given;

@Test
public void testGetEntityV1() {
    given()
        .header("Accept", MediaType.APPLICATION_JSON)
    .when()
        .get("/basepath/1")
    .then()
        .assertThat()
        ... // Some check that Version 1 was called
    ;
}

@Test
public void testGetEntityV1OldClientNoAcceptHeader() {
    get("/basepath/1")
        .then()
        .assertThat()
        ... // Some check that Version 1 was called
    ;
}

@Test
public void testGetEntityV2() {
    given()
        .header("Accept", "application/vnd.COMPANY.systeminfo-v2+json")
    .when()
        .get("/basepath/1")
    .then()
        .assertThat()
        ... // Some check that Version 2 was called
    ;
}
票数 1
EN

Stack Overflow用户

发布于 2011-02-07 17:09:15

一个可能的解决方案是使用一个@路径

内容-类型: application/vnd.COMPANY.systeminfo-{version}+json

然后,在给定@路径的方法中,您可以调用WebService的版本

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4924034

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档