《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 JAVA日知录 「飘渺Jam」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

在实际项目开发中我们经常需要对接口进行版本管理。那今天我们就来聊聊为什么需要版本控制,以及如何对REST API进行版本控制。我们将讨论4种版本控制的方法,并比较不同的方法。

通过此文您将学到

  • 为什么我们需要对RESTful API 进行版本控制?
  • 可用的版本控制有哪些?
  • 如何实现基于 Restful 的版本控制?

为什么我们需要对RESTful API进行版本化

最好的版本控制方法是不进行版本控制。只要不需要版本控制,就不要版本控制。

构建向后兼容的服务,以便尽可能避免版本控制!

然而,在许多情况下我们都需要进行版本控制,然我们看看下面具体的例子:

最初,你有个这个版本的Student服务,返回数据如下:

{
"name": "Bob Charlie"
}

后来,您希望将学生的名字拆分,因此创建了这个版本的服务。

{
"name": {
"firstName": "Bob",
"lastName": "Charlie"
}
}

您可以从同一个服务支持这两个请求,但是随着每个版本的需求多样化,它会变得越来越复杂。

在这种情况下,版本控制就成必不可少,强制性的了。

接下来让我们创建一个简单的SpringBoot的maven项目,并理解对 RESTful 服务进行版本控制的4种不同方法。

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

几个用于实现版本控制的Bean

第一个版本的 Bean

@Data
@AllArgsConstructor
public class StudentV1 {
private String name;
}

第二个版本的 Bean

@Data
public class StudentV2 {
private Name name;
}

StudentV2使用的Name实体

@Data
@AllArgsConstructor
public class Name {
private String firstName;
private String lastName;
}

Restful 版本控制的方法

我们希望创建两个版本的服务,一个返回 StudentV1,另一个返回 StudentV2。

让我们来看看创建相同服务版本的4种不同方法。

通过 URI 进行版本控制

@RestController
public class StudentUriController {

@GetMapping("v1/student")
public StudentV1 studentV1() {
return new StudentV1("javadaily");
}

@GetMapping("v2/student")
public StudentV2 studentV2() {
return new StudentV2(new Name("javadaily", "JAVA日知录"));
}

}

请求:http://localhost:8080/v1/student

响应:{“name”:”javadaily”}

请求:http://localhost:8080/v2/student

响应:{“name”:{“firstName”:”javadaily”,”lastName”:”JAVA日知录”}}

通过请求参数进行版本控制

版本控制的第二种方法是使用请求参数来区分版本。请求示例如下所示:

  • http://localhost:8080/student/param?version=1
  • http://localhost:8080/student/param?version=2

实现方式如下:

@RestController
public class StudentParmController {

@GetMapping(value="/student/param",params = "version=1")
public StudentV1 studentV1() {
return new StudentV1("javadaily");
}

@GetMapping(value="/student/param",params = "version=2")
public StudentV2 studentV2() {
return new StudentV2(new Name("javadaily", "JAVA日知录"));
}
}

请求:http://localhost:8080/student/param?version=1

响应:{“name”:”javadaily”}

请求:http://localhost:8080/student/param?version=2

响应:{“name”:{“firstName”:”javadaily”,”lastName”:”JAVA日知录”}}

通过自定义Header进行版本控制

版本控制的第三种方法是使用请求头来区分版本,请求示例如下:

  • http://localhost:8080/student/header

    • headers=[X-API-VERSION=1]
  • http://localhost:8080/student/header

    • headers=[X-API-VERSION=2]

实现方式如下所示:

@RestControllerpublic class StudentHeaderController {    @GetMapping(value="/student/header",headers = "X-API-VERSION=1")    public StudentV1 studentV1() {        return new StudentV1("javadaily");    }    @GetMapping(value="/student/header",headers = "X-API-VERSION=2")    public StudentV2 studentV2() {        return new StudentV2(new Name("javadaily", "JAVA日知录"));    }}

下图展示了我们如何使用Postman执行带有请求头的Get请求方法。

请求:http://localhost:8080/student/header
header:X-API-VERSION = 1

请求:http://localhost:8080/student/header
header:X-API-VERSION = 2

通过媒体类型进行版本控制

最后一种版本控制方法是在请求中使用Accept Header,请求示例如下:

  • http://localhost:8080/student/produce

    • headers=[Accept=application/api-v1+json]
  • http://localhost:8080/student/produce

    • headers=[Accept=application/api-v2+json]

实现方式如下:

@RestController
public class StudentProduceController {

@GetMapping(value="/student/produce",produces = "application/api-v1+json")
public StudentV1 studentV1() {
return new StudentV1("javadaily");
}

@GetMapping(value="/student/produce",produces = "application/api-v2+json")
public StudentV2 studentV2() {
return new StudentV2(new Name("javadaily", "JAVA日知录"));
}
}

下图展示了我们如何使用Postman执行带有请求Accept的Get方法。

请求:http://localhost:8080/student/produce
header:Accept = application/api-v1+json

请求:http://localhost:8080/student/produce
header:Accept = application/api-v2+json

影响版本选择的因素

以下因素影响版本控制的选择

  • URI 污染 - URL版本和请求参数版本控制会污染URI空间。
  • 滥用请求头 - Accept 请求头并不是为版本控制而设计的。
  • 缓存 - 如果你使用基于头的版本控制,我们不能仅仅基于URL缓存,你需要考虑特定的请求头。
  • 是否能在浏览器直接执行 ? - 如果您有非技术消费者,那么基于URL的版本将更容易使用,因为它们可以直接在浏览器上执行。
  • API文档 - 如何让文档生成理解两个不同的url是同一服务的版本?

事实上,并没有完美的版本控制解决方案,你需要根据项目实际情况进行选择。

下面列表展示了主要API提供商使用的不同版本控制方法:

  • 媒体类型的版本控制

    • Github
  • 自定义Header

    • Microsoft
  • URI路径

    • Twitter,百度,知乎
  • 请求参数控制

    • Amazon

好了,今天的文章就到这里了,希望能对你有所帮助。

文章目录
  1. 1. 为什么我们需要对RESTful API进行版本化
    1. 1.1. 几个用于实现版本控制的Bean
  2. 2. Restful 版本控制的方法
    1. 2.1. 通过 URI 进行版本控制
    2. 2.2. 通过请求参数进行版本控制
    3. 2.3. 通过自定义Header进行版本控制
    4. 2.4. 通过媒体类型进行版本控制
  3. 3. 影响版本选择的因素