首页 文章

API版本控制的最佳实践? [关闭]

提问于
浏览
878

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

我注意到AWS does versioning by the URL of the endpoint . 这是唯一的方式还是有其他方法来实现同一目标?如果有多种方式,每种方式的优点是什么?

7 回答

  • 684

    这是一个很好而且棘手的问题 . URI design is 的主题同时 the most prominent part of a REST API and ,因此,可能 long-term commitment towards the users of that API .

    由于应用程序的演变以及在较小程度上它的API是生活中的事实,并且它甚至类似于看似复杂的产品(如编程语言)的演变, URI design 应该更少 natural constraints 而且 should be preserved over time . 应用程序生命周期越长,对应用程序和API用户的承诺就越大 .

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

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

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

    此外,由于将API版本嵌入到URI中会破坏hypermedia as the engine of application state(在Roy T.Fieldings博士论文中陈述)的概念,因为资源地址/ URI会随着时间的推移而变化,我会得出结论 API versions should not be kept in resource URIs for a long time 意味着 resource URIs that API users can depend on should be permalinks .

    当然, it is possible to embed API version in base URIonly for reasonable and restricted uses like debugging a API client 适用于新的API版本 . 此类版本化API应该是有时间限制的,并且仅限于有限的API用户组(例如在封闭的测试版中) . 否则,你会把自己投入到你不应该做的地方 .

    有关维护API版本的一些想法,这些API版本具有到期日期 . 通常用于实现Web服务(Java,.NET,PHP,Perl,Rails等)的所有编程平台/语言允许将Web服务 endpoints 轻松绑定到基URI . 这样很容易 gather and keep 文件/类/方法 separate across different API versions 的集合 .

    从API用户POV开始,当它显而易见但仅在有限的时间内(即在开发期间)时,它也更容易使用并绑定到特定的API版本 .

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

    但是,在URI中清晰可见的API版本有一个警告:人们也可能反对这种方法,因为 API history becomes visible/aparent in the URI design and therefore is prone to changes over time 违反了REST的指导原则 . 我同意!

    解决这个合理异议的方法是在无版本API基URI下实现最新的API版本 . 在这种情况下,API客户端开发人员可以选择:

    • 针对最新版本进行开发(承诺维护应用程序以保护它免受可能破坏其 badly designed API client 的最终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版本 if the API version they're using is obsolete or not supported anymore . 因此,访问任何过时的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
    

    应该返回与 Location HTTP标头一起使用的任何 30x HTTP status codes that indicate redirection ,它重定向到仍然属于这个的资源URI的适当版本:

    http://shonzilla/api/customers/1234
    

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

    • 301 Moved permanently指示具有请求的URI的资源永久移动到另一个URI(应该是不包含API版本信息的资源实例永久链接) . 此状态代码可用于指示过时/不受支持的API版本,通知API客户端 versioned resource URI been replaced by a resource permalink .

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

    • 其他场景可以在Redirection 3xx chapter of HTTP 1.1 specification

  • 99

    我同意对资源表示进行版本控制更好地遵循REST方法...但是,自定义MIME类型(或附加版本参数的MIME类型)的一个大问题是在HTML中写入Accept和Content-Type标头的支持不佳JavaScript的 .

    例如,为了创建资源,IMO不可能使用HTML5表单中的以下 Headers 进行POST:

    Accept: application/vnd.company.myapp-v3+json
    Content-Type: application/vnd.company.myapp-v3+json
    

    这是因为HTML5 enctype 属性是一个枚举,因此除了通常的 application/x-www-formurlencodedmultipart/form-datatext/plain 之外的任何内容都是无效的 .

    ...我也不确定HTML4中的所有浏览器都支持它(它具有更宽松的encytpe属性,但是关于MIME类型是否被转发会是浏览器实现问题)

    因此我觉得最合适的版本是通过URI,但我接受它不是'正确'的方式 .

  • 46

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

    如果查看具有版本的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>
    

    Headers 包含包含您要求的表示的行("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中 . 假设您想要在对象内部获得一些信息,并且您已经对各种对象进行了版本化(客户是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/
    
  • 13

    我们发现将版本放在URL中是实用且有用的 . 它可以让您轻松了解您正在使用的内容 . 我们使用别名/ foo到/ foo /(最新版本)以便于使用,更短/更清晰的URL等,正如接受的答案所暗示的那样 .

    永远保持向后兼容通常是成本过高和/或非常困难的 . 我们更愿意提前通知弃用,重定向此处建议,文档和其他机制 .

  • 8

    对REST API进行版本控制类似于任何其他API的版本控制 . 可以进行微小的更改,主要更改可能需要一个全新的API . 对您来说最简单的方法是每次都从头开始,这时将URL中的版本放在最合适的位置 . 如果您想让客户端更轻松,那么您可以尝试保持向后兼容性,这可以通过弃用(永久重定向),多个版本的资源等来实现 . 这更加繁琐,需要更多努力 . 但它也是REST鼓励“酷URI不会改变”的东西 .

    最后,它就像任何其他API设计一样 . 权衡客户方便性 . 考虑为您的API采用语义版本控制,这使您的客户清楚地知道新版本的向后兼容性 .

  • 21

    您可以在REST API中进行版本控制的一些地方:

    • 如上所述,在URI中 . 如果使用重定向等,这可以是易处理的,甚至是美学上令人愉悦的 .

    • 在Accepts: Headers 中,因此版本在文件类型中 . 比如'mp3'vs'mp4' . 这也可行,虽然IMO的工作效果不如......

    • 在资源本身 . 许多文件格式都嵌入了它们的版本号,通常在 Headers 中;这允许更新的软件通过了解所有现有的“正常工作”文件类型的版本,而较旧的软件可以在指定不受支持(较新)的版本时发挥作用 . 在REST API的上下文中,这意味着您的URI永远不必更改,只需要您对所交付的特定数据版本的响应 .

    我可以看到使用这三种方法的原因:

    • 如果你喜欢做'clean sweep'新的API,或者你想要这种方法的主要版本更改 .

    • 如果您希望客户端在进行PUT / POST之前知道它是否能够正常工作 .

    • 如果's okay if the client has to do its PUT/POST to find out if it'开始工作 .

  • 273

    将您的版本放在URI中 . API的一个版本并不总是支持另一个版本的类型,因此资源仅从一个版本迁移到另一个版本的论点是完全错误的 . 这与将格式从XML切换到JSON不同 . 这些类型可能不存在,或者它们可能在语义上发生了变化 .

    版本是资源地址的一部分 . 您正在从一个API路由到另一个API . 在 Headers 中隐藏寻址并不是RESTful .

相关问题