如何版本REST URI


111

REST URI版本的最佳方法是什么?当前,我们在URI本身中有一个版本号,即。

http://example.com/users/v4/1234/

对于此表示形式的版本4。

版本是否属于queryString?即。

http://example.com/users/1234?version=4

还是以另一种方式最好地完成了版本控制?


Answers:


34

我会说最好将它作为URI本身的一部分(选项1),因为v4标识的资源不同于v3。像您在第二个选项中那样的查询参数最适合用于传递与请求相关的其他(查询)信息,而不是资源


11
问题是,我们正在讨论的资源是否不同?还是该资源的另一种表示形式?REST是否区分表示形式和资源?
Cheeso

1
@Cheeso-OP表示它是不同的表示形式而不是不同的资源,因此是我的答案。
格雷格·比奇

在此之前,这已得到更详细的解答stackoverflow.com/q/389169/104261
Taras Alenin'5

为“ +1查询参数(如您的第二个选项中的参数,最好用于传递与请求相关的其他(查询)信息而不是资源)” + 1
andy

对于不同的表示形式,我认为您应该使用诸如“ Accept”之类的标头,然后客户端可以向服务器指定“我仅接受版本4”,服务器可以使用该表示形式进行回答。如果未发送接受,则提供最新版本。
卡洛斯·维德斯

190

不要版本化URL,因为...

  • 你打破固定链接
  • 网址更改将像疾病一样通过您的界面传播。您如何处理未更改但指向已更改的表示呢?如果更改URL,则会破坏旧客户端。如果您保留网址,则新客户可能无法使用。
  • 对媒体类型进行版本控制是一种更加灵活的解决方案。

假设您的资源返回了application / vnd.yourcompany.user + xml的某种变体,那么您需要做的就是为新的application / vnd.yourcompany.userV2 + xml媒体类型创建支持,并通过内容协商的魔力来实现v1和v2客户可以和平共处。

在RESTful接口中,与合同最接近的就是定义客户端与服务器之间交换的媒体类型。

客户端用于与服务器交互的URL应由嵌入在先前检索的表示形式中的服务器提供。客户端唯一需要知道的URL是接口的根URL。仅当您在客户端上构造url时,才将版本号添加到url中才有价值,而这并不是您想使用RESTful接口实现的。

如果您需要更改将破坏现有客户的媒体类型,那么请创建一个新的客户,不要理会您的网址!

对于当前那些读者来说,如果我使用application / xml和application / json作为媒体类型,这毫无意义。我们应该如何对这些版本进行版本控制?你不是。除非您使用代码下载来解析它们,否则这些媒体类型对于RESTful接口几乎是无用的,在这一点上,版本控制才是关键。


66
解决要点。1.您不会破坏永久链接,因为永久链接链接到特定版本。2.如果所有版本均已版本化,则这不是问题。旧网址仍然可以使用。理想情况下,您不希望使用版本4 URL返回与版本3资源的关联。3.也许
Mike Pone 09年

10
想象一下,如果升级到Web浏览器的新版本时,所有带有书签的收藏夹都损坏了!请记住,从概念上讲,用户是在保存指向资源的链接,而不是指向资源表示形式的链接。
Darrel Miller

11
@Gili为了满足REST api自描述的要求,内容类型标头必须提供消息的完整语义描述。换句话说,您的媒体类型就是您的数据合同。如果提供了application / xml或application / json,则不会告诉客户端该XML / Json中包含的内容。客户端应用程序到达您正在创建的耦合/客户/名称的瞬间,该耦合基于消息中未包含的信息。消除带外耦合对于实现RESTfulness至关重要。
Darrel Miller

6
@Gili除了根URL之外,客户端应该没有其他有关API URL的知识。您不应将表示形式的格式绑定到特定的URL。当谈到选择你真的需要一个特定的格式如应用/ vnd.mycompany.myformat + XML或者标准的人喜欢,XHTML,原子,RDF等之间进行选择的媒体类型
达雷尔-米勒

4
将API版本放在单独的标头字段中是否有意义?像这样:接受:application / com.example.myapp + json; 版本= 1.0
Erik

21

啊,我又戴上了我那脾气暴躁的旧帽子。

从ReST的角度来看,这根本没有关系。不是香肠

客户端收到它要遵循的URI,并将其视为不透明字符串。将任何内容放入其中,客户端都不知道版本标识符之类的内容。

客户知道它可以处理媒体类型,我建议您遵循Darrel的建议。我个人还认为,需要四次更改宁静的架构中使用的格式,这会带来巨大的警告信号,表明您在做严重错误的事情,并完全绕过了设计介质类型以提高变化弹性的需求。

但是,无论哪种方式,客户端都只能以其可以理解的格式处理文档,并遵循其中的链接。它应该知道链接关系(过渡)。因此,URI中的内容完全不相关。

我个人会投票支持http:// localhost / 3f3405d5-5984-4683-bf26-aca186d21c04

一个完全有效的标识符,它将防止任何其他客户端开发人员或其他人接触系统,以质疑是否应将v4放在URI的开头或结尾(并且我建议从服务器的角度来看,您不应有4个版本,但有4种媒体类型)。


如果表示需要显着更改并且不向后兼容怎么办?
2009年

1
通过以可扩展的方式设计媒体类型,例如使用名称空间和可扩展的xsd或现有的xml格式(例如ike atom),应该可以避免这种情况。如果确实需要,则可以采用另一种媒体类型。
SerialSeb

1
我喜欢这个完全有效的答案,但我认为所提议的URI比起确实需要“可破解” URI的真实场景更能证明这一点。
戴夫·范·登·艾因德

10

您不应将版本放在URL中,而应将版本放在请求的“接受标头”中-在此线程上查看我的文章:

API版本控制的最佳做法?

如果您开始在URL中粘贴版本,则最终会得到类似以下的愚蠢URL:http : //company.com/api/v3.0/customer/123/v2.0/orders/4321/

此外,还有许多其他问题在蔓延-请参阅我的博客:http : //thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html


11
抱歉,但我认为您不会得到像这样的愚蠢URL。您将版本号绑定到特定资源或(更糟)绑定到特定表示。IMO,那太愚蠢了。而是要对API进行版本控制,因此URI中永远不会有多个版本。
fool4jesus


3

有4种不同的API版本控制方法:

  • 将版本添加到URI路径:

    http://example.com/api/v1/foo
    
    http://example.com/api/v2/foo
    

    当您进行重大更改时,必须增加版本,例如:v1,v2,v3 ...

    您可以在代码中实现一个控制器,如下所示:

    @RestController
    public class FooVersioningController {
    
    @GetMapping("v1/foo")
    public FooV1 fooV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping("v2/foo")
    public FooV2 fooV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • 请求参数版本控制:

    http://example.com/api/v2/foo/param?version=1
    http://example.com/api/v2/foo/param?version=2
    

    版本参数可以是可选参数,也可以是必需参数,具体取决于您希望API的使用方式。

    实现可以与此类似:

    @GetMapping(value = "/foo/param", params = "version=1")
    public FooV1 paramV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/param", params = "version=2")
    public FooV2 paramV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • 传递自定义标头:

    http://localhost:8080/foo/produces
    

    带标题:

    headers[Accept=application/vnd.company.app-v1+json]
    

    要么:

    headers[Accept=application/vnd.company.app-v2+json]
    

    该方案的最大优点是语义:不会使URI与版本控制有关。

    可能的实现:

    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v1+json")
    public FooV1 producesV1() {
        return new FooV1("firstname lastname");
    }
    
    @GetMapping(value = "/foo/produces", produces = "application/vnd.company.app-v2+json")
    public FooV2 producesV2() {
        return new FooV2(new Name("firstname", "lastname"));
    }
    
  • 更改主机名或使用API​​网关:

    本质上,您要将API从一个主机名移到了另一个。您甚至可以调用此方法为相同的资源构建一个新的API。

    另外,您可以使用API​​网关执行此操作。


2

如果REST服务在使用前需要身份验证,则可以轻松地将API密钥/令牌与API版本关联,并在内部进行路由。要使用API​​的新版本,可能需要链接到该版本的新API密钥。

不幸的是,该解决方案仅适用于基于身份验证的API。但是,它的确将版本保留在URI之外。



1

我会将版本作为可选值包括在URI的末尾。它可以是/ V4之类的后缀,也可以是您所描述的查询参数。您甚至可以将/ V4重定向到查询参数,因此您同时支持这两种变体。


1

如果使用URI进行版本控制,则版本号应位于API根目录的URI中,因此每个资源标识符都可以包含该版本号。

从技术上讲,REST API不会因URL更改(统一接口约束的结果)而中断。仅当相关语义(例如,特定于API的RDF vocab)以非向后兼容方式(罕见)更改时,它才会中断。当前,许多ppl不使用链接进行导航(HATEOAS约束)和vocab注释其REST响应(自描述消息约束),这就是其客户端中断的原因。

自定义MIME类型和MIME类型版本控制无济于事,因为将相关的元数据和表示的结构放入短字符串中不起作用。Ofc。元数据和结构会经常更改,因此版本号也是如此。

因此,回答您的问题的最佳方法是用词汇表(Hydra链接数据)注释您的请求和响应,并忘记版本控制或仅通过不向后兼容的词汇表更改来使用它(例如,如果您要用另一个替代词汇表)。


0

我赞成使用MIME类型而不是URL进行此操作。但是原因与其他人不同。

我认为该URL应该是唯一的(那些重定向除外),以查找唯一资源。因此,如果您接受/v2.0URL,为什么不接受/ver2.0or /v2/或or /v2.0.0?甚至-alpha-beta?(然后它完全成为semver的概念)

因此,MIME类型的版本比URL更可接受。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.