如何版本REST URI

Mik*_*one 106 versioning rest clean-urls

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

http://example.com/users/v4/1234/
Run Code Online (Sandbox Code Playgroud)

对于此表示的第4版.

该版本是否属于queryString?即.

http://example.com/users/1234?version=4
Run Code Online (Sandbox Code Playgroud)

或者版本控制最好的另一种方式?

Dar*_*ler 191

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

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

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

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

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

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

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

  • 解决要点.1.你没有打破perma链接,因为永久链接链接到特定版本2.如果一切都是版本化的,这不是问题.旧的网址仍然可以工作.理想情况下,您不希望版本4 URL返回与版本3资源的关联.也许吧 (65认同)
  • @Gili为了满足REST api自描述的要求,内容类型头必须提供消息的完整语义描述.换句话说,您的媒体类型就是您的数据合同.如果您提供application/xml或application/json,则告诉客户端没有关于XML/Json中包含的内容.客户端应用程序在拉出/ Customer/Name中到达的瞬间,您正在创建基于消息中不存在的信息的耦合.消除带外耦合对于实现RESTful性至关重要. (11认同)
  • 想象一下,如果当您升级到新版本的Web浏览器时,所有已添加书签的收藏夹都会破碎!请记住,从概念上讲,用户正在保存资源的链接,而不是资源表示的版本. (10认同)
  • @Gili除了根URL之外,客户端应该不知道API的URL.您不应将表示格式与特定URL绑定.在选择媒体类型时,您确实需要在特定格式(如application/vnd.mycompany.myformat + xml)或标准格式(如XHtml,Atom,RDF等)之间进行选择. (6认同)
  • 将API版本作为单独的标题字段是否有意义?像这样:接受:application/com.example.myapp + json; 版本= 1.0 (4认同)
  • @Darrel,URL和XML/JSON的组合是不是告诉你预期的确切格式?如果没有,你会推荐什么? (2认同)
  • @Gili 问题是每个请求都应该是独立的自我描述的。如果您复制页面 B 的 URL 并通过电子邮件发送给朋友,那么您就失去了上下文。另一个问题是,为了定义页面 A 的规范,您无论如何都需要创建自定义媒体类型。 (2认同)
  • @Gili您假设该服务仅返回单个媒体类型.想象一下支持`application/vnd.acme.document`和'application/vnd.acme.validationrules'的服务.每个"文档"指向包含"validationrules"的另一个资源.通过版本化媒体类型,我可以通过引入`application/vnd.acme.validationrulesV2`来处理对验证规则的重大更改,而无需创建新版本的'application/vnd.acme.document' (2认同)
  • @Gili不确定我是否跟进.没有URI被烘焙.客户端也不知道将从任何URI返回什么媒体类型.客户应该根据回来的内容决定做什么.它只知道的是,它知道如何处理应用程序/ vnd.ResourceAv2和应用程序/ vnd.ResourceBv1所以它发接受:应用/ vnd.ResourceAv2,应用/ vnd.ResourceBv1每一个URI,它要求. (2认同)
  • 我不知道你可以定义自己的媒体类型; 我假设有标准(如application/xml和application/json).像泽西岛这样的框架由于像媒体类型枚举这样的事情而引发了这种谬论. (2认同)

Zef*_*mel 33

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

  • 问题是,我们正在讨论的是不同的资源吗?或者该资源的不同表示?REST是否区分了表示和资源? (11认同)

Ser*_*Seb 21

啊,我又把旧的脾气暴躁的帽子放了.

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

客户端接收它想要遵循的URI,并将其视为不透明字符串.把你想要的东西放在里面,客户端就不知道它上面有版本标识符这样的东西.

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

但无论哪种方式,客户端只能处理其可以理解的格式的文档,并遵循其中的链接.它应该知道链接关系(转换).那么URI中的内容完全无关紧要.

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

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


jer*_*myh 11

你不应该把版本放在URL中,你应该把版本放在请求的Accept Header中 - 请看我在这个帖子上的帖子:

API版本控制的最佳实践?

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

  • 对不起,但我认为你最终没有像这样的愚蠢网址.您将版本号绑定到特定资源或(更糟糕)绑定到特定表示.那将是愚蠢的,IMO.相反,您正在对API进行版本控制,因此您在URI中永远不会有多个版本. (10认同)

Pet*_*aat 5

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


Jav*_* C. 5

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

  • 将版本添加到 URI 路径:

    http://example.com/api/v1/foo
    
    http://example.com/api/v2/foo
    
    Run Code Online (Sandbox Code Playgroud)

    当您有重大更改时,您必须增加版本,例如: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"));
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 请求参数版本控制:

    http://example.com/api/v2/foo/param?version=1
    http://example.com/api/v2/foo/param?version=2
    
    Run Code Online (Sandbox Code Playgroud)

    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"));
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 传递自定义标头:

    http://localhost:8080/foo/produces
    
    Run Code Online (Sandbox Code Playgroud)

    带标题:

    headers[Accept=application/vnd.company.app-v1+json]
    
    Run Code Online (Sandbox Code Playgroud)

    或者:

    headers[Accept=application/vnd.company.app-v2+json]
    
    Run Code Online (Sandbox Code Playgroud)

    这种方案的最大优势主要是语义:您不会因与版本控制有关的任何事情而使 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"));
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 更改主机名或使用 API 网关:

    本质上,您正在将 API 从一个主机名移动到另一个主机名。您甚至可以将其称为构建针对相同资源的新 API。

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