How to version REST URIs

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

http://example.com/users/v4/1234/

此表示的版本4。

版本是否属于 queryString。

http://example.com/users/1234?version=4

或者,版本控制最好是通过另一种方式完成的?

63188 次浏览

我认为将其作为 URI 本身的一部分(选项1)是最好的,因为 v4标识的资源与 v3不同。像第二个选项中的查询参数可以最好地用于传入与 请求相关的附加(查询)信息,而不是 资源

我会在 URI 的末尾将版本作为一个可选值包含进来。这可以是像/V4这样的后缀,也可以是您所描述的查询参数。您甚至可以将/V4重定向到查询参数,以便同时支持这两种变体。

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

Do not version URLs, because ...

  • 你破坏了永久链接
  • Url 的变化会像疾病一样通过你的界面传播。如何处理没有改变但指向已经改变的表示?如果你更改网址,你会破坏旧客户端。如果您离开网址,您的新客户端可能无法工作。
  • Versioning media types is a much more flexible solution.

Assuming that your resource is returning some variant of application/vnd.yourcompany.user+xml all you need to do is create support for a new application/vnd.yourcompany.userV2+xml media type and through the magic of content negotiation your v1 and v2 clients can co-exist peacefully.

在 RESTful 接口中,与契约最接近的是客户机和服务器之间交换的媒体类型的定义。

客户机用于与服务器交互的 URL 应该由嵌入在以前检索到的表示中的服务器提供。客户端需要知道的唯一 URL 是接口的根 URL。向 urls 添加版本号只有在客户机上构造 url 时才有价值,而 RESTful 接口不应该这样做。

如果您需要改变您的媒体类型,这将打破您现有的客户端,然后创建一个新的,离开您的网址!

And for those readers currently saying that this makes no sense if I am using application/xml and application/json as media-types. How are we supposed to version those? You're not. Those media-types are pretty much useless to a RESTful interface unless you parse them using code-download, at which point versioning is a moot point.

啊,我又戴上了我那顶脾气暴躁的旧帽子。

From a ReST perspective, it doesn't matter at all. Not a sausage.

客户端接收到一个它希望遵循的 URI,并将其视为一个不透明的字符串。放入任何你想要的东西,客户端有 没有的知识作为它的版本标识符。

What the client knows is that it can process the media type, and I'll advise to follow Darrel's advice. Also I personally feel that needing to change the format used in a restful architecture 4 times should bring huge massive warning signs that you're doing something seriously wrong, and completely bypassing the need to design your media type for change resiliance.

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

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

这是一个完全有效的标识符,可以防止任何客户端开发人员或用户进一步触及系统,质疑是否应该将 v4放在 URI 的开头或结尾(我建议,从服务器的角度来看,你不应该有4个版本,而是4种媒体类型)。

你不应该把版本放在网址里,你应该把版本放在请求的接受标题里——看看我在这个帖子上的帖子:

Best practices for API versioning?

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

如果 REST 服务在使用之前需要身份验证,那么您可以轻松地将 API 密钥/令牌与 API 版本关联起来,并在内部执行路由。要使用新版本的 API,可能需要一个新的 API 密钥,并链接到该版本。

不幸的是,这个解决方案只适用于基于身份的 API。

我想创建版本化的 API,我发现这篇文章非常有用:

http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http

其中有一小部分是关于“我希望对我的 API 进行版本控制”的。我觉得这很简单,也很容易理解。关键是使用标题中的 Accept 字段来传递版本信息。

如果使用 URI 进行版本控制,那么版本号应该位于 API 根的 URI 中,这样每个资源标识符都可以包含它。

从技术上讲,RESTAPI 不会因 URL 更改(统一接口约束的结果)而中断。只有当相关语义(例如 API 特定的 RDF 词汇表)以非向后兼容的方式发生变化时(很少) ,它才会中断。目前,许多 ppl 不使用导航链接(HATEOAS 约束)和词汇表来注释它们的 REST 响应(自描述消息约束) ,这就是它们的客户端中断的原因。

自定义 MIME 类型和 MIME 类型版本控制没有帮助,因为将相关元数据和表示的结构放入一个短字符串中是不起作用的。办公室。元数据和结构会经常变化,所以版本号也会随之变化。

So to answer your question the best way to annotate your requests and responses with vocabs (九头蛇, linked data) and forget versioning or use it only by non backward compatible vocab changes (for example if you want to replace a vocab with another one).

我投票赞成这样做,在 mime 类型,但不在网址。 但原因和其他人不一样。

我认为 URL 应该是唯一的(除了那些重定向)来定位唯一的资源。 So, if you accept /v2.0 in URLs, why it is not /ver2.0 or /v2/ or /v2.0.0? Or even -alpha and -beta? (then it totally becomes the concept of 永远)

因此,mime 类型的版本比 URL 更容易接受。

There are 4 different approaches to versioning the API:

  • Adding version to the URI path:

    http://example.com/api/v1/foo
    
    
    http://example.com/api/v2/foo
    

    当您有重大变化时,您必须增加版本,如: 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"));
    }
    
  • Request parameter versioning:

    http://example.com/api/v2/foo/param?version=1
    http://example.com/api/v2/foo/param?version=2
    

    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"));
    }
    
  • Passing a custom header:

    http://localhost:8080/foo/produces
    

    With header:

    headers[Accept=application/vnd.company.app-v1+json]
    

    or:

    headers[Accept=application/vnd.company.app-v2+json]
    

    这个方案的最大优点主要是语义: 您不会将 URI 与与版本控制有关的任何事情混杂在一起。

    Possible implementation:

    @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"));
    }
    
  • Changing Hostnames or using API Gateways:

    Essentially, you’re moving the API from one hostname to another. You might even just call this building a new API to the same resources.

    Also,you can do this using API Gateways.