我已经实现了一个解决方案,可以完美地处理其余版本的问题。
一般说来,有3种主要的REST版本控制方法:
基于路径的方法,其中客户端在URL中定义版本:
http://localhost:9001/api/v1/user
http://localhost:9001/api/v2/user
Content-Type标头,其中客户端在Accept标头中定义版本:
http://localhost:9001/api/v1/user with
Accept: application/vnd.app-1.0+json OR application/vnd.app-2.0+json
自定义标头,其中客户端在自定义标头中定义版本。
该问题与第一个方法是,如果你改变的版本,让我们从V1说- > V2,也许你需要复制,粘贴并没有改变,以V2路径V1资源
该问题与第二个做法是,一些工具,如http://swagger.io/可以用相同的路径,但不同的内容类型(支票发放操作之间没有明显的https://github.com/OAI/OpenAPI-Specification/issues/ 146)
解决方案
由于我经常使用其余的文档工具,因此我更喜欢使用第一种方法。我的解决方案使用第一种方法处理了该问题,因此您无需将端点复制粘贴到新版本。
假设我们有User控制器的v1和v2版本:
package com.mspapant.example.restVersion.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* The user controller.
*
* @author : Manos Papantonakos on 19/8/2016.
*/
@Controller
@Api(value = "user", description = "Operations about users")
public class UserController {
/**
* Return the user.
*
* @return the user
*/
@ResponseBody
@RequestMapping(method = RequestMethod.GET, value = "/api/v1/user")
@ApiOperation(value = "Returns user", notes = "Returns the user", tags = {"GET", "User"})
public String getUserV1() {
return "User V1";
}
/**
* Return the user.
*
* @return the user
*/
@ResponseBody
@RequestMapping(method = RequestMethod.GET, value = "/api/v2/user")
@ApiOperation(value = "Returns user", notes = "Returns the user", tags = {"GET", "User"})
public String getUserV2() {
return "User V2";
}
}
的要求是,如果我请求V1为用户资源我必须采取“用户V1” repsonse,否则如果我请求V2,V3等我必须采取“用户V2”响应。
为了在春季实现此功能,我们需要覆盖默认的RequestMappingHandlerMapping行为:
package com.mspapant.example.restVersion.conf.mapping;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class VersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Value("${server.apiContext}")
private String apiContext;
@Value("${server.versionContext}")
private String versionContext;
@Override
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
HandlerMethod method = super.lookupHandlerMethod(lookupPath, request);
if (method == null && lookupPath.contains(getApiAndVersionContext())) {
String afterAPIURL = lookupPath.substring(lookupPath.indexOf(getApiAndVersionContext()) + getApiAndVersionContext().length());
String version = afterAPIURL.substring(0, afterAPIURL.indexOf("/"));
String path = afterAPIURL.substring(version.length() + 1);
int previousVersion = getPreviousVersion(version);
if (previousVersion != 0) {
lookupPath = getApiAndVersionContext() + previousVersion + "/" + path;
final String lookupFinal = lookupPath;
return lookupHandlerMethod(lookupPath, new HttpServletRequestWrapper(request) {
@Override
public String getRequestURI() {
return lookupFinal;
}
@Override
public String getServletPath() {
return lookupFinal;
}});
}
}
return method;
}
private String getApiAndVersionContext() {
return "/" + apiContext + "/" + versionContext;
}
private int getPreviousVersion(final String version) {
return new Integer(version) - 1 ;
}
}
该实现读取URL中的版本并从spring请求解析该URL。如果此URL不存在(例如客户端请求v3),则尝试使用v2,直到找到该资源的最新版本。
为了看到此实现的好处,我们有两个资源:User和Company:
http://localhost:9001/api/v{version}/user
http://localhost:9001/api/v{version}/company
假设我们对公司的“合同”进行了更改,这改变了客户的利益。因此我们实现了,http://localhost:9001/api/v2/company
并要求客户端将其更改为v2,而不是v1。
因此,来自客户端的新请求是:
http://localhost:9001/api/v2/user
http://localhost:9001/api/v2/company
代替:
http://localhost:9001/api/v1/user
http://localhost:9001/api/v1/company
这里最好的部分是,使用此解决方案,客户端将从v1获取用户信息,从v2 获取公司信息,而无需从用户v2创建新的(相同)端点!
其余文档
正如我在选择基于URL的版本控制方法之前所说的那样,某些工具(如swagger)不会以相同的URL记录不同的端点,但是内容类型却不同。使用此解决方案,由于具有不同的URL,因此将显示两个端点:
GIT
解决方案的实现位于:https :
//github.com/mspapant/restVersioningExample/