API版本的最佳实践?

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

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

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

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

另一方面,生活的另一个事实是,很难预见通过API将消耗的所有资源和方面。 幸运的是,没有必要devise将被使用的整个API,直到启示录 。 正确定义每个资源和资源实例的所有资源终点和寻址scheme就足够了。

随着时间的推移,您可能需要为每个特定的资源添加新的资源和新的属性,但是一旦资源编址scheme变为公开,API用户访问特定资源的方法不应该改变。

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

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

当然, 可以在基本URI中embeddedAPI版本,只能用于合理和受限制的用途,如debugging与新API版本一起工作的API客户端 。 这种版本化的API应该是有时间限制的,并且只能用于有限的API用户组(比如在封闭testing期间)。 否则,你在自己不应该做的地方做出承诺。

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

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

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

但是,在URI中清晰可见的API版本中有一个警告:也许还会反对这种方法,因为API历史在URIdevise中变得可见/不可见 ,因此随着时间的推移会发生变化,这违背了REST的指导原则。 我同意!

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

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

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

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

 HTTP:// shonzilla / API /客户/ 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/ec/1234
 http:// shonzilla / api / v2 / customers / 1234
 http:// shonzilla / api /v1.1/customers/1234
 http:// shonzilla / api / v1 / customers / 1234

应返回任何指示redirect30x HTTP状态代码,这些代码Location HTTP标头一起使用,redirect到资源URI的适当版本,该版本仍然是这个:

 HTTP:// shonzilla / API /客户/ 1234

至less有两个适用于API版本控制场景的redirectHTTP状态代码:

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

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

  • 其他场景可以在HTTP 1.1规范的redirect3xx章节中find

该url不应该包含版本。 该版本与您所请求资源的“构思”无关。 你应该尝试把URL看作是你想要的概念的path – 而不是你想要的项目返回。 版本决定了对象的表示,而不是对象的概念。 正如其他海报所说,你应该在请求头中指定格式(包括版本)。

如果您查看具有版本的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,并且必须parsing才能意识到这是错误的。

更好的方法是:

 (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"} } 

使用这种方法,您在devise上有了更多的自由,并且实际上坚持了REST的原始思想。 您可以在不中断客户端的情况下更改版本,或者在API更改时增量更改客户端。 如果您select停止支持表示,则可以使用HTTP状态代码或自定义代码响应请求。 客户端也可以validation响应格式是否正确,并validationXML。

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

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

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

我们发现把这个版本放在URL中是很实用和有用的。 它可以很容易地告诉你一目了然。 正如我们接受的答案所暗示的,我们使用/ foo / foo /(最新版本)来简化使用,缩短/更清晰的URL等等。

永远保持向后兼容通常是成本高昂的和/或非常困难的。 我们更愿意提前通知弃用,像这里build议的redirect,文档和其他机制。

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

例如,为了创build资源,IMO不可能使用HTML5表单中的以下标题进行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属性,但对于MIMEtypes是否被转发将是浏览器实现问题)

正因为如此,我现在觉得最适合版本的方法是通过URI,但我认为这不是“正确的”方式。

把你的版本放在URI里。 API的一个版本并不总是支持另一个版本,所以资源只是从一个版本迁移到另一个版本的说法显然是错误的。 这与从XML切换到JSON格式不一样。 types可能不存在,或者它们可能已经在语义上改变。

版本是资源地址的一部分。 你从一个API路由到另一个。 在标题中隐藏寻址不是RESTful。

有几个地方可以在REST API中进行版本控制:

  1. 如上所述,在URI中。 如果redirect等使用得当,这可以是容易理解的,甚至是美观的。

  2. 在“接受:”标题中,版本位于文件types中。 像“MP3”与“MP4”。 这也将工作,虽然海事组织工作有点比… …

  3. 在资源本身。 许多文件格式的版本号都embedded其中,通常在标题中; 这允许较新的软件通过理解文件types的所有现有版本来“正常工作”,而如果指定了不支持的(较新的)版本,则较旧的软件可能会崩溃。 在REST API的上下文中,这意味着您的URI永远不会改变,只是您对所提交数据的特定版本的响应。

我可以看到使用所有三种方法的理由:

  1. 如果您喜欢“干净地扫描”新的API,或者需要这种方法的主要版本更改。
  2. 如果你希望客户在做PUT / POST之前知道它是否会工作。
  3. 如果客户端必须执行PUT / POST来确定它是否能够正常工作。

REST API的版本化与其他API的版本化类似。 可以做一些小的改动,主要的改变可能需要一个全新的API。 最简单的方法就是每次都要从头开始,这就是将版本放在URL中是最有意义的。 如果你想让客户的生活变得更容易,你可以尝试保持向后兼容性,你可以通过弃用(永久redirect),多个版本的资源等来实现这种兼容性。这样做比较麻烦,需要付出更多的努力。 但是这也是REST鼓励在“酷URI不会改变”。

最后它就像任何其他的APIdevise一样。 重视客户的便利。 考虑为您的API采用语义版本化,这使您的客户清楚了新版本的向后兼容性。