版本REST API(Versioning REST API)

2019-06-24 18:16发布

看了关于REST版本了大量的材料后,我thnking版本的调用,而不是API的。 例如:

http://api.mydomain.com/callfoo/v2.0/param1/param2/param3
http://api.mydomain.com/verifyfoo/v1.0/param1/param2

代替具有第一

http://api.mydomain.com/v1.0/callfoo/param1/param2
http://api.mydomain.com/v1.0/verifyfoo/param1/param2

然后去

http://api.mydomain.com/v2.0/callfoo/param1/param2/param3
http://api.mydomain.com/v2.0/verifyfoo/param1/param2

优点我看到的是:

  • 当通话变化,我没有重写我的整个客户端 - 只有被改变的呼叫的影响部分。
  • 这项工作好可以继续为客户端的那些部分(我们有很多的投入测试小时,以确保客户端和服务器双方是稳定的。)
  • 我可以使用永久或非永久重定向已更改来电。
  • 向后兼容性将是一件轻而易举的事,因为我可以离开旧呼入版本不变。

我失去了财产以后? 请指教。

Answer 1:

Require an HTTP header.

Version: 1

The Version header is provisionally registered in RFC 4229 and there some legitimate reasons to avoid using an X- prefix or a usage-specific URI. A more typical header was proposed by yfeldblum at https://stackoverflow.com/a/2028664:

X-API-Version: 1

In either case, if the header is missing or doesn't match what the server can deliver, send a 412 Precondition Failed response code along with the reason for the failure. This requires clients to specify the version they support every single time but enforces consistent responses between client and server. (Optionally supporting a ?version= query parameter would give clients an extra bit of flexibility.)

This approach is simple, easy to implement and standards-compliant.

Alternatives

I'm aware that some very smart, well-intentioned people have suggested URL versioning and content negotiation. Both have significant problems in certain cases and in the form that they're usually proposed.

URL Versioning

Endpoint/service URL versioning works if you control all servers and clients. Otherwise, you'll need to handle newer clients falling back to older servers, which you'll end up doing with custom HTTP headers because system administrators of server software deployed on heterogeneous servers outside of your control can do all sorts of things to screw up the URLs you think will be easy to parse if you use something like 302 Moved Temporarily.

Content Negotiation

Content negotiation via the Accept header works if you are deeply concerned about following the HTTP standard but also want to ignore what the HTTP/1.1 standard documents actually say. The proposed MIME Type you tend to see is something of the form application/vnd.example.v1+json. There are a few problems:

  1. There are cases where the vendor extensions are actually appropriate, of course, but slightly different communication behaviors between client and server doesn't really fit the definition of a new 'media type'. Also, RFC 2616 (HTTP/1.1) reads, "Media-type values are registered with the Internet Assigned Number Authority. The media type registration process is outlined in RFC 1590. Use of non-registered media types is discouraged." I don't want to see a separate media type for every version of every software product that has a REST API.
  2. Any subtype ranges (e.g., application/*) don't make sense. For REST APIs that return structured data to clients for processing and formatting, what good is accepting */* ?
  3. The Accept header takes some effort to parse correctly. There's both an implied and explicit precedence that should be followed to minimize the back-and-forth required to actually do content negotiation correctly. If you're concerned about implementing this standard correctly, this is important to get right.
  4. RFC 2616 (HTTP/1.1) describes the behavior for any client that does not include an Accept header: "If no Accept header field is present, then it is assumed that the client accepts all media types." So, for clients you don't write yourself (where you have the least control), the most correct thing to do would be to respond to requests using the newest, most prone-to-breaking-old-versions version that the server knows about. In other words, you could have not implemented versioning at all and those clients would still be breaking in exactly the same way.

Edited, 2014:

I've read a lot of the other answers and everyone's thoughtful comments; I hope I can improve on this with the benefit of a couple of years of feedback:

  1. 不要使用“X-”的前缀 。 我想Accept-Version大概是在2014年更有意义,大概还有重新使用的语义一些有效的担忧Version在意见中提出的。 有伴的定义头像的重叠Content-Version和URI的肯定相对不透明,而且我尽量小心与内容协商变化,这将混淆这两个Version头是有效。 URL的第三“版本” https://example.com/api/212315c2-668d-11e4-80c7-20c9d048772b比“第二”完全不同的,不管它是否包含数据或文档。
  2. 至于我上面关于URL版本说(如端点https://example.com/v1/users ,例如)反过来可能拥有更多的真理:如果你控制的所有服务器和客户端,URL / URI版本可能是你想要什么。 对于一个大型的服务,可以发布一个服务URL,我会用不同的端点去每一个版本, 最喜欢做的 。 我特别采取的是严重的事实,上述的实现是由许多不同组织的最常用部署在许多不同的服务器上,而且,也许最重要的是,在服务器上我无法控制的影响。 我总是希望规范服务的URL,如果一个网站仍在运行的API V3版本,我绝对不希望的请求https://example.com/v4/回来与他们的网络服务器404不发现页面(或者更糟,200 OK,返回他们的主页作为HTML通过蜂窝数据传回一个iPhone应用程序的50万。)
  3. 如果你想很简单/客户/实现(和更广泛地采用),这是非常很难说,需要在HTTP请求自定义标题是用于客户端的作者那样简单GET -ting香草URL。 (虽然认证通常需要您的令牌或凭据在头传递,反正。使用VersionAccept-Version实际的秘密握手沿着一个秘密的握手配合得很好。)
  4. 使用内容协商Accept头部有利于让不同的MIME类型相同的内容(例如,XML与JSON对比的Adobe PDF),但这些东西版本(都柏林核心1.1与JSONP与PDF / A没有定义)。 如果你想支持Accept头,因为它尊重的行业标准是非常重要的,那么你不会想要一个虚构的MIME类型,您可能需要在您的请求使用的媒体类型协商干扰。 一个定制的API版本头保证不会干扰的大量使用的,经常被引用的Accept ,而他们混为一谈到相同的使用也只是用于服务器和客户端的混乱。 这就是说,命名空间你希望为每2013的命名剖面RFC6906最好有许多原因一个单独的头。 这是非常聪明的, 我认为人们应该认真考虑这种做法
  5. 添加为每个请求的报头是一个特定缺点无状态协议中工作。
  6. 恶意代理服务器可以做几乎任何事情破坏HTTP请求和响应。 他们不应该 ,虽然我不谈论的缓存控制或会发生变化在这种情况下头,所有服务的创造者应该仔细考虑自己的内容是如何在许多不同的环境的消耗。


Answer 2:

这是见仁见智的问题; 这里是我的,背后意见的动机一起。

  1. 在URL中包含的版本。
    对于那些谁说,它属于在HTTP头,我说: 也许吧。 但投入的URL是接受的方式,根据在该领域的早期领导人这样做。 (谷歌,雅虎,Twitter和更多)。 这是开发商预计,做什么开发商预计,换句话说,按照最小惊讶的原则行事,可能是一个好主意。 它绝对不会让“很难为客户升级”。 如果URL的变化在某种程度上代表了一个障碍消费应用的开发者,如在不同的答案这里建议,即开发人员需要被解雇。

  2. 跳过次要版本
    有很多的整数。 你不会用完。 你不需要在那里小数。 从1.0到您的API 1.1的任何变化不应该反正打破现有的客户。 因此,只要使用自然数。 如果你喜欢用分离暗示较大的变化,你可以在V100开始做V200等,但即使在那里,我认为YAGNI和它的矫枉过正。

  3. 把最左边版本的URI
    大概有将要多的资源模型。 他们都需要同步进行版本。 你不能使用资源X的V1人,和资源Y.的V2这将打破一些东西。 如果试图支持,当你添加的版本它会创建一个维护的噩梦,而且也没有增加价值为开发反正。 所以, http://api.mydomain.com/v1/Resource/12345 ,其中Resource是资源的类型,以及12345得到由资源ID代替。

你没问,但...

  1. 从你的URL路径省略动词
    REST是面向资源的。 你有喜欢的东西“CallFoo”在你的URL路径,它看起来很像一个动词,而不像一个名词。 这是错误的。 使用部队,卢克。 使用REST是部分的动词:GET PUT POST DELETE等。 如果你想获得对资源的验证,那么就GET http://domain/v1/Foo/12345/verification 。 如果您想更新它,做POST /v1/Foo/12345

  2. 把可选PARAMS作为查询参数或有效载荷
    可选的PARAMS不应该在URL路径(第一问号之前),除非你是说,那些可选PARAMS构成自常备资源。 所以, POST /v1/Foo/12345?action=partialUpdate&param1=123&param2=abc



Answer 3:

不要做任何的那些东西,因为他们推入版本的URI结构,而这将会为您的客户端应用程序缺点。 这将使他们更难升级到利用新的功能,在您的应用程序。

相反,你应该版本的媒体类型,而不是你的URI。 这会给你最大的灵活性和进化的能力。 欲了解更多信息,请参阅这个答案我给了另一个问题。



Answer 4:

我喜欢用型材媒体类型参数:

application/json; profile="http://www.myapp.com/schema/entity/v1"

更多信息:

http://tools.ietf.org/html/rfc6906

http://buzzword.org.uk/2009/draft-inkster-profile-parameter-00.html



Answer 5:

这取决于你叫什么版本的API中,如果调用版本的实体的不同表示(XML,JSON等),那么你应该使用接受标题或自定义标题。 这是HTTP是设计用于表示工作的方式。 它是基于REST的,因为如果我把在同一时间同一资源,但请求不同的表示,返回的实体将拥有完全相同的信息和财产结构,但不同的格式,这种版本的是化妆品。

如果您知道“版本”作为实体结构添加字段“年龄”到“用户”实体的变化,例如另一方面。 然后,你应该从资源的角度来看这是在我看来,RESTful方法处理这个。 正如他在disseration由Roy Fielding描述......一个REST资源是从标识到一组实体的映射......因此是有道理的,改变实体的结构时,你需要有一个指向一个适当的资源版。 这种版本是结构性的。

我做了一个类似的评论: http://codebetter.com/howarddierking/2012/11/09/versioning-restful-services/

当URL的工作版本的版本应该晚一点,而不是在网址前面:

GET/DELETE/PUT onlinemall.com/grocery-store/customer/v1/{id}
POST onlinemall.com/grocery-store/customer/v1

这样做,在一个更清洁的方式,但另一种方式实现时可能是有问题的:

GET/DELETE/PUT onlinemall.com/grocery-store/customer.v1/{id}
POST onlinemall.com/grocery-store/customer.v1

这样做,这样允许客户端专门请求映射到他们所需要的实体,他们所需的资源。 无需与页眉和自定义介质类型在生产环境中实现时的确是有问题的混乱。

还具有URL url中后期允许客户有更多的粒度,即使在方法层面专门选择他们想要的资源时,。

但是从开发人员的角度最重要的事情,你不需要为每一个版本的整个映射(路径)保持对所有的资源和方法。 这是非常宝贵的,当你有很多的子资源(嵌入式资源)。

从资源的水平上具有它实现的角度来看是很容易实现,例如,如果使用泽西/ JAX-RS:

@Path("/customer")
public class CustomerResource {
    ...
    @GET
    @Path("/v{version}/{id}")
    public IDto getCustomer(@PathParam("version") String version, @PathParam("id") String id) {
         return locateVersion(version, customerService.findCustomer(id));
    }
    ...
    @POST
    @Path("/v1")
    @Consumes(MediaType.APPLICATION_JSON)
    public IDto insertCustomerV1(CustomerV1Dto customer) {
         return customerService.createCustomer(customer);
    }

    @POST
    @Path("/v2")
    @Consumes(MediaType.APPLICATION_JSON)
    public IDto insertCustomerV2(CustomerV2Dto customer) {
         return customerService.createCustomer(customer);
    }
...
}

IDto只是返回一个多态对象,CustomerV1和CustomerV2实现该接口的接口。



Answer 6:

Facebook并verisoning 的URL 。 我觉得URL版本是更清洁,更容易在现实世界中,保持为好。

.NET使得超级容易做到的版本是这样的:

[HttpPost]
[Route("{version}/someCall/{id}")]
public HttpResponseMessage someCall(string version, int id))


文章来源: Versioning REST API
标签: api rest