API版本控制的最佳实践?

是否有任何已知的Web服务REST API版本的方法或最佳实践?

我注意到AWS使用端点的URL进行版本控制。 这是唯一的方法还是有其他方法来实现相同的目标? 如果有多种方式,每种方式的优点是什么?


这是个好问题,也是一个棘手的问题。 URI设计的主题同时也是REST API中最重要的部分 ,因此对该API的用户可能是一个长期的承诺

由于应用程序的演变以及其在较小程度上的API是生活中的事实,并且它与看似复杂的产品(如编程语言)的演变甚至相似,所以URI设计应该具有较少的自然约束 ,并且应该保留随着时间 。 应用程序和API的使用寿命越长,对应用程序和API用户的承诺就越大。

另一方面,生活的另一个事实是,很难预见通过API将会消耗的所有资源及其方面。 幸运的是,没有必要设计将在Apocalypse之前使用的整个API。 正确定义每个资源和资源实例的所有资源端点和寻址方案就足够了。

随着时间的推移,您可能需要为每个特定的资源添加新的资源和新的属性,但是一旦资源寻址方案变为公开并且因此是最终的,则API用户访问特定资源的方法不应改变。

此方法适用于早期API版本支持的HTTP动词语义(例如,PUT应该始终更新/替换)和HTTP状态代码(它们应该继续工作,以便在没有人为干预的情况下工作的API客户端应该能够继续工作像那样)。

此外,由于将API版本嵌入URI将通过具有随时间改变的资源地址/ URI而将超媒体的概念作为应用程序状态的引擎(在Roy T.Fieldings博士论文中陈述)中断,我会得出结论: API版本不应长期保存在资源URI中,这意味着API用户可以依赖的资源URI应该是永久链接

当然, 可以将API版本嵌入到基本URI中,仅适用于合理且受限制的用途,例如调试与新API版本配合使用的API客户端 。 这种版本化的API应该是有时间限制的,并且仅供有限组的API用户使用(如在封闭测试期间)。 否则,你在不该做的地方犯下自己的错误。

关于维护其版本过期的API版本有一些想法。 通常用于实现Web服务(Java,.NET,PHP,Perl,Rails等)的所有编程平台/语言都允许将Web服务端点简单绑定到基本URI。 通过这种方式,很容易收集并保存 不同API版本的文件/类/方法的集合。

从API用户POV中可以看出,只有在有限的时间内,即在开发过程中,使用并绑定到特定的API版本也更容易。

从API维护者的POV中,通过使用主要在文件上工作的源代码控制系统作为(源代码)版本控制的最小单元,可以更容易地并行维护不同的API版本。

但是,在URI中清晰可见的API版本中存在一个警告:由于API历史在URI设计中变得可见/不同 ,因此可能会反对这种方法,因此随着时间的推移 ,这种情况会随着REST的指导而变化。 我同意!

绕过这个合理的反对意见的方法是在无版本的API基础URI下实现最新的API版本。 在这种情况下,API客户端开发人员可以选择:

  • 针对最新版本开发(承诺保持应用程序免受可能破坏其糟糕设计的API客户端的最终API更改)。

  • 绑定到特定版本的API(这变得明显),但仅限于有限的时间

  • 例如,如果API v3.0是最新的API版本,则以下两个应该是别名(即,与所有API请求的行为相同):

    http://shonzilla/api/customers/1234
    http://shonzilla/api/v3.0/customers/1234
    http://shonzilla/api/v3/customers/1234
    

    另外,如果仍然尝试指向旧API的API客户端使用的API版本已过时或不再受支持,则应通知其使用最新的先前API版本。 因此,访问任何这样的过时的URI:

    http://shonzilla/api/v2.2/customers/1234
    http://shonzilla/api/v2.0/customers/1234
    http://shonzilla/api/v2/customers/1234
    http://shonzilla/api/v1.1/customers/1234
    http://shonzilla/api/v1/customers/1234
    

    应返回任何指示重定向30x HTTP状态代码,这些代码Location HTTP标头一起使用,该标头可重定向到适当版本的资源URI,该资源URI仍为下图所示:

    http://shonzilla/api/customers/1234
    

    至少有两个适用于API版本控制场景的重定向HTTP状态代码:

  • 301永久移动,表明具有请求URI的资源永久移动到另一个URI(应该是不包含API版本信息的资源实例永久链接)。 此状态码可用于指示过时/不受支持的API版本,通知API客户端版本化的资源URI已被资源永久链接替代

  • 302发现指示请求的资源暂时位于另一个位置,而请求的URI可能仍然受支持。 当无版本的URI暂时不可用并且应该使用重定向地址重复请求(例如指向嵌入了APi版本的URI)并且我们想要告诉客户端继续使用它时,此状态码可能很有用(即固定链接)。

  • 其他场景可以在HTTP 1.1规范的重定向3xx章节中找到


  • 该网址不应包含版本。 该版本与您所请求资源的“想法”无关。 您应该尝试将网址视为您想要的概念的路径 - 而不是您希望该项目返回的方式。 版本决定了对象的表示形式,而不是对象的概念。 正如其他海报所说,你应该在请求头中指定格式(包括版本)。

    如果您查看具有版本的URL的完整HTTP请求,它看起来像这样:

    (BAD WAY TO DO IT):
    
    http://company.com/api/v3.0/customer/123
    ====>
    GET v3.0/customer/123 HTTP/1.1
    Accept: application/xml
    
    <====
    HTTP/1.1 200 OK
    Content-Type: application/xml
    <customer version="3.0">
      <name>Neil Armstrong</name>
    </customer>
    

    标题包含包含您要求的表示的行(“Accept:application / xml”)。 这是版本应该去的地方。 每个人似乎都忽视了这样一个事实,即您可能需要不同格式的相同内容,并且客户应该能够询问它想要的内容。 在上面的例子中,客户端正在询问资源的任何XML表示 - 并不真正代表它想要的内容。 理论上,服务器可以返回与请求完全无关的内容,只要它是XML并且必须解析才能意识到它是错误的。

    更好的方法是:

    (GOOD WAY TO DO IT)
    
    http://company.com/api/customer/123
    ===>
    GET /customer/123 HTTP/1.1
    Accept: application/vnd.company.myapp.customer-v3+xml
    
    <===
    HTTP/1.1 200 OK
    Content-Type: application/vnd.company.myapp-v3+xml
    <customer>
      <name>Neil Armstrong</name>
    </customer>
    

    此外,可以说客户认为XML过于冗长,现在他们想要JSON。 在其他例子中,你必须为同一个客户建立一个新的URL,所以你最终会得到:

    (BAD)
    http://company.com/api/JSONv3.0/customers/123
      or
    http://company.com/api/v3.0/customers/123?format="JSON"
    

    (或类似的东西)。 实际上,每个HTTP请求都包含您正在查找的格式:

    (GOOD WAY TO DO IT)
    ===>
    GET /customer/123 HTTP/1.1
    Accept: application/vnd.company.myapp.customer-v3+json
    
    <===
    HTTP/1.1 200 OK
    Content-Type: application/vnd.company.myapp-v3+json
    
    {"customer":
      {"name":"Neil Armstrong"}
    }
    

    使用这种方法,您在设计上拥有更多的自由,并且实际上坚持了REST的原始思想。 您可以在不中断客户端的情况下更改版本,或在API更改时递增更改客户端。 如果您选择停止支持表示,您可以使用HTTP状态代码或自定义代码对请求进行响应。 客户端还可以验证响应格式是否正确,并验证XML。

    还有很多其他的优点,我在我的博客上讨论其中的一些:http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

    最后一个例子说明如何将URL放入URL中。 假设你想在对象内部获得一些信息,并且你已经对你的各种对象进行了版本管理(客户是v3.0,订单是v2.0,shipto对象是v4.2)。 这里是你必须在客户端提供的令人讨厌的URL:

    (Another reason why version in the URL sucks)
    http://company.com/api/v3.0/customer/123/v2.0/orders/4321/
    

    我们发现将该版本放在URL中是实用且有用的。 它可以让您一目了然地分辨出您正在使用的内容。 正如我们接受的答案所暗示的,我们使用易于使用的alias / foo / foo /(最新版本),更短/更清晰的URL等。

    永远保持向后兼容性通常是成本过高和/或非常困难的。 我们倾向于提前发布弃用通知,如此处所述的重定向,文档和其他机制。

    链接地址: http://www.djcxy.com/p/3739.html

    上一篇: Best practices for API versioning?

    下一篇: Do sessions really violate RESTfulness?