RESTful Web服务版本控制 - 使用URI的基本方法

版本控制是API中最重要且最具挑战性的部分,因为它需要向后兼容的API。版本控制有助于在识别更改时更快地进行迭代。我们应该始终为我们的Web API进行版本控制。

考虑这样一种情况:我们有一个正在运行的Web API。用户正在使用该API。现在,我们想在Web API中添加更多功能,但希望保持现有功能不变。可能仍有一些用户希望使用旧的API,而其他用户则希望获得具有新功能或扩展功能的新版本的API。这就是Web API版本控制出现的情况。

什么时候需要版本控制:

当我们在Web API中进行了破坏性的更改时,我们应该对API进行版本控制。破坏性更改包括:

  • 更改一个或多个调用的响应数据格式。
  • 更改响应类型。
  • 删除API的任何部分。

破坏性更改应该始终导致API或内容响应类型的主版本号发生变化。

非破坏性更改(添加新点或新的响应参数)不需要对主版本号进行更改。但是,追踪API的次版本号可能会有所帮助。

如何进行版本控制

最常用的方法分为三类:

  • URI版本控制
  • 使用自定义请求头进行版本控制
  • 使用Accept头进行版本控制

URI版本控制

URI版本控制是最直接的方法。它在URL中指定为查询字符串。它违反了URI应该引用唯一资源的原则。当版本更新时,您还确保会中断客户端集成。Twitter 使用URI版本控制。

示例
http://api.demo.com/v1 http://apiv1.demo.com

版本号不需要是数字,也不需要使用v[x]语法来指定。还可以选择日期、项目名称、季节或其他在版本更改时有意义的标识符。

使用自定义请求头进行版本控制

自定义头使我们可以保留URL。它是现有Accept头实现的内容协商行为的副本。版本信息在不需要更改URL的情况下在请求头中指定。Microsoft 使用请求头版本控制。用户无法在正常浏览器(Chrome)中访问请求头版本控制。我们需要一个特殊的插件才能在浏览器上访问它们。

示例

Accept-version: v1 Accept-version: v2

使用Accept头进行版本控制

Accept头定义媒体类型和字符编码。我们还可以通过accept头传递Web API的版本信息,而无需更改URL。这也称为媒体类型版本控制、内容协商或接受头版本控制。GitHub 使用Accept头版本控制。用户无法在正常浏览器(Chrome)中访问接受头版本控制。我们需要一个特殊的插件才能在浏览器上访问它们。

示例

Accept: application/vnd.demo.v1+json Accept:application/vnd.demo+json;version=1.0

让我们看看如何在项目中实现版本控制。

URI版本控制

步骤1:*在包*cn.javatiku.server.main.versioning中创建一个名为PersonV1.java的类。PersonV1表示API的第一个版本。初始版本的API具有一个名为“name”的变量。

PersonV1.java

package cn.javatiku.server.main.versioning;  
public class PersonV1   
{  
private String name;  
}  

步骤2:*随着时间的推移,我们认识到需要单独的名字和姓氏。因此,我们创建了一个名为*Person2.java的类。它表示API的第二个版本。

PersonV2.java

package cn.javatiku.server.main.versioning;  
public class PersonV2   
{  
private Name name;  
}  

步骤3:*创建一个名为*Name.java的类,该类具有两个单独的变量firstNamelastName

Name.java

package cn.javatiku.server.main.versioning;  
public class Name   
{  
private String firstName;  
private String lastName;  
}  

旧版本仍然返回完整的名字,而第二个版本返回了单独的firstName和lastName。现在,我们需要为同一个服务创建两个不同的版本。

让我们看看如何创建同一个服务的两个不同版本以及存在哪些不同版本的服务。

步骤4:*在*Name.java文件中,生成Getters和Setters,使用字段生成构造函数。创建一个类的无参构造函数Name。

Name.java

package cn.javatiku.server.main.versioning;  
public class Name   
{  
private String firstName;  
private String lastName;  
//no argument constructor  
public Name()   
{  
  
}  
public Name(String firstName, String lastName)   
{  
super();  
this.firstName = firstName;  
this.lastName = lastName;  
}  
public String getFirstName()   
{  
return firstName;  
}  
public void setFirstName(String firstName)   
{  
this.firstName = firstName;  
}  
public String getLastName()   
{  
return lastName;  
}  
public void setLastName(String lastName)   
{  
this.lastName = lastName;  
}  
}  

步骤5:*打开*PersonV1.java类。生成Getters和Setters,使用字段生成构造函数。创建一个类的无参构造函数PersonV1.java。

PersonV1.java

package cn.javatiku.server.main.versioning;  
public class PersonV1   
{  
private String name;  
//no argument constructor  
public PersonV1()   
{  
super();      
}  
public PersonV1(String name)   
{  
super();  
this.name = name;  
}  
public String getName()   
{  
return name;  
}  
public void setName(String name)   
{  
this.name = name;  
}  
}  

步骤6:打开PersonV2.java。生成Getters和Setters,使用字段生成构造函数。创建一个类的无参构造函数PersonV2.java。

PersonV2.java

package cn.javatiku.server.main.versioning;  
public class PersonV2   
{  
private Name name;  
public PersonV2()   
{  
super();  
}  
public PersonV2(Name name)   
{  
super();  
this.name = name;  
}  
public Name getName()   
{  
return name;  
}  
public void setName(Name name)   
{  
this.name = name;  
}  
}  

现在,我们需要创建一个服务。

步骤7:*创建一个名为*PersonVersioningController.java的类。为不同版本创建两个方法,并将它们映射到不同的URI。

PersonVersioningController.java

package cn.javatiku.server.main.versioning;  
import org.springframework.web.bind.annotation.RestController;  
import org.springframework.web.bind.annotation.GetMapping;  
@RestController  
public class PersonVersoningController   
{  
//this method is for the first version that returns the entire name  
@GetMapping("v1/person")  
public PersonV1 personv1()  
{  
return new PersonV1("Tom Cruise");  
}  
//this method is for the second version that returns firstName and lastName separately  
@GetMapping("v2/person")  
public PersonV2 personv2()  
{  
return new PersonV2(new Name("Tom", "Cruise"));  
}  
}  

步骤8:*打开*Postman,并发送一个带有URI http://localhost:8080/v1/person 的GET请求。它返回完整的名字,如下图所示。

restful-web-services-versioning.png

将URI 更改为 http://localhost:8080/v2/person,以获得第二个版本。它返回firstName和lastName,如下图所示。

restful-web-services-versioning2.png

使用请求参数进行版本控制

实现版本控制的另一种方法是使用请求参数。亚马逊使用请求参数版本控制。打开PersonVersioningController.java并进行以下更改:

  • 将第一个方法的URI从/v1/person 更改为/person/param。
  • 将方法名称 personV1 更改为 paramV1。
  • 同样,将第二个方法的URI从/v2/person 更改为/person/param。

两个方法具有相同的获取映射,我们将使用value和params属性来区分它们。value属性包含我们要使用的URI,params属性包含区分版本之间的参数。

PersonVersioningController.java

package cn.javatiku.server.main.versioning;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RestController;  
@RestController  
public class PersonVersoningController   
{  
//this method is for first version that returns the entire name  
@GetMapping(value="/person/param", params="version=1")  
public PersonV1 personV1()  
{  
return new PersonV1("Tom Cruise");  
}  
//this method is for second version that returns firstName and lastName separately  
@GetMapping(value="/person/param", params="version=2")  
public PersonV2 personV2()  
{  
return  new PersonV2(new Name("Tom", "Cruise"));  
}  
}

现在,移动到Postman并发送带有URI http://localhost:8080/person/param?version=1 的GET请求。它返回完整的名字,如下图所示。

restful-web-services-versioning3.png

再次发送一个GET请求,以获取版本2。为此,我们需要将Headers标签下的值从1更改为2。它返回firstName和lastName,如下图所示。

restful-web-services-versioning4.png

使用请求头进行版本控制

进行版本控制的另一种选项是使用请求头。这类似于内容协商。在此方法中,我们通过使用Request Header来区分服务。

PersonVersioningController.java文件中,进行以下操作:

  • 复制两个方法并粘贴到同一个文件中。
  • 将方法名称 paramV1 更改为 headerV1,将 paramV2 更改为 headerV2。
  • 将URI /person/param 更改为 /person/header,并将 params 更改为 headers。

    /---------------using request header--------------/
    //this method is for first version that returns the entire name
    @GetMapping(value="/person/header", headers="X-API-Version=1")
    public PersonV1 headerV1()
    {
    return new PersonV1("Tom Cruise");
    }
    //this method is for second version that returns firstName and lastName separately
    @GetMapping(value="/person/header", headers="X-API-Version=2")
    public PersonV2 headerV2()
    {
    return new PersonV2(new Name("Tom", "Cruise"));
    }

打开Postman

  • 选择Headers标签,设置键:X-API-Version,值:1。
  • 在Headers标签下取消选择X-API-VERSION键。
  • 输入URI http://localhost:8080/person/header,并发送GET请求。

它返回名字完整的名字。

restful-web-services-versioning5.png

让我们为版本2发送一个GET请求。为此,我们需要将X-API-VERSION的值从1更改为2。

它返回firstName和lastName,如下图所示。

restful-web-services-versioning6.png

使用Accept Header进行版本控制

另一种版本控制的方法是使用Accept Header。它也被称为内容协商或Accept版本控制。在此方法中,我们使用一个名为produce的属性。它表示我们为特定服务生成何种输出。

PersonVersioningController.java文件中,进行以下操作:

  • 复制两个方法并粘贴到同一个文件中。
  • 将方法名称 headerV1 更改为 producesV1,将 headerV2 更改为 producesV2。
  • 将URI /person/header 更改为 /person/produces,并将 header 更改为 produces。

    /---------------using accept header--------------/
    //this method is for first version that returns the entire name
    @GetMapping(value="/person/produces", produces="application/vnd.company.app-v1+json")
    public PersonV1 producesV1()
    {
    return new PersonV1("Tom Cruise");
    }
    //this method is for second version that returns firstName and lastName separately
    @GetMapping(value="/person/produces", produces="application/vnd.company.app-v2+json")
    public PersonV2 producesV2()
    {
    return new PersonV2(new Name("Tom", "Cruise"));
    }

打开Postman

  • 选择Headers标签,设置键:Accept,值:application/vnd.company.app-v1+json。
  • 不选中X-API-VERSION键。
  • 输入URI http://localhost:8080/person/produces,并发送GET请求。

它返回完整的名字。

restful-web-services-versioning7.png

让我们为版本2发送一个GET请求。为此,我们需要将Accept的值从application/vnd.company.app-v1+json 更改为application/vnd.company.app-v2+json。

它返回firstName和lastName,如下图所示。

restful-web-services-versioning8.png

标签: spring, Spring教程, Spring语言学习, Spring框架, Spring框架教程, Spring框架高级教程, spring boot, spring boot入门教程, spring boot学习教程, spring boot下载, spring boot框架入门, spring boot面试题, spring boot笔试题, spring boot学习指南, spring boot技术