如何版本REST URI

时间:2009-06-09 20:06:36

标签: rest versioning clean-urls

版本REST URI的最佳方法是什么?目前我们在URI中有一个版本#,即

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

表示此表示的第4版。

该版本是否属于queryString?即

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

或者版本控制最好用另一种方式完成?

11 个答案:

答案 0 :(得分:191)

不要版本网址,因为......

  • 你打破永久链接
  • 网址更改将通过您的界面像疾病一样传播。你如何处理没有改变的表示但指向具有的表示?如果您更改了网址,则会破坏旧客户端。如果您保留该网址,您的新客户可能无法使用。
  • 版本化媒体类型是一种更灵活的解决方案。

假设您的资源正在返回application / vnd.yourcompany.user + xml的某些变体,您需要做的就是创建对新应用程序/ vnd.yourcompany.userV2 + xml媒体类型的支持,并通过内容协商的魔力您的v1和v2客户端可以和平共存。

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

客户端用于与服务器交互的URL应由嵌入在先前检索的表示中的服务器提供。客户端需要知道的唯一URL是接口的根URL。如果您在客户端上构建URL,那么将版本号添加到URL仅具有值,您不应该使用RESTful接口。

如果您需要更改将破坏现有客户的媒体类型,请创建一个新的并保留您的网址!

对于那些读者来说,如果我使用application / xml和application / json作为媒体类型,这是没有意义的。我们该怎么做那些版本?你不是。这些媒体类型对RESTful接口来说几乎没用,除非你使用代码下载解析它们,此时版本控制是没有实际意义的。

答案 1 :(得分:30)

我想说它是URI本身(选项1)的一部分是最好的,因为v4识别的资源不同于v3。查询参数(如第二个选项)最适合传入与请求相关的其他(查询)信息,而不是资源

答案 2 :(得分:21)

啊,我又把我那老脾气暴的帽子放了。

从ReST的角度来看,它根本不重要。不是香肠。

客户端收到它想要关注的URI,并将其视为不透明字符串。把你想要的东西放在里面,客户端没有知道它上面的版本标识符。

客户知道它可以处理媒体类型,我建议遵循Darrel的建议。另外我个人觉得需要4次更改宁静架构中使用的格式应该会带来巨大的警告信号,表明你正在做一些严重错误的事情,并完全绕过设计媒体类型以获得变更补偿的需要。

但不管怎样,客户端只能处理一个可以理解的格式的文档,并按照其中的链接进行操作。它应该知道链接关系(转换)。所以URI中的内容完全不相关。

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

一个完全有效的标识符,可以防止任何进一步的客户开发人员或触摸系统的人质疑是否应该将v4放在URI的开头或末尾(我建议,从服务器的角度来看,你不应该'有4个版本,但有4种媒体类型。)

答案 3 :(得分:11)

您不应该将版本放在URL中,您应该将版本放在请求的Accept Header中 - 请参阅此帖子中的帖子:

Best practices for API versioning?

如果你开始在网址中粘贴版本,你最终会得到如下的愚蠢网址: 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

答案 4 :(得分:5)

关于REST API版本控制的这些(不太具体的)SO问题可能会有所帮助:

答案 5 :(得分:2)

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

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

答案 6 :(得分:2)

我想创建版本化的API,我发现这篇文章非常有用:

http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http

“我希望我的API版本化”中有一小部分。我发现它简单易懂。关键是在标题中使用Accept字段来传递版本信息。

答案 7 :(得分:1)

我将该版本作为URI末尾的可选值包含在内。这可能是像/ V4这样的后缀或者你所描述的查询参数。您甚至可以将/ V4重定向到查询参数,以便支持这两种变体。

答案 8 :(得分:1)

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

从技术上讲,REST API不会因URL更改而中断(统一接口约束的结果)。只有当相关语义(例如API特定的RDF词汇)以非向后兼容的方式(罕见)发生变化时,它才会中断。目前很多人不使用导航链接(HATEOAS约束)和词汇来注释他们的REST响应(自我描述性消息约束),这就是他们的客户破坏的原因。

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

因此,回答您的问题是使用词汇(Hydralinked data)注释您的请求和回复的最佳方式,并忘记版本控制或仅使用非向后兼容的词汇更改(例如,如果您想用另一个词汇代替词汇。)

答案 9 :(得分:1)

有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
    
      

    version参数可以是可选参数,也可以是必需参数,具体取决于您希望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​​网关进行此操作。

答案 10 :(得分:0)

我投票支持在mime类型中执行此操作但不在URL中。 但原因与其他人不一样。

我认为用于定位唯一资源的URL应该是唯一的(除了那些重定向)。 那么,如果您接受了网址中的/v2.0,为什么它不是/ver2.0/v2//v2.0.0?甚至是-alpha-beta? (然后它完全成为semver

的概念

因此,mime类型的版本比URL更容易接受。