背景: 最近有个需求,在接口设计阶段将版本放进了HTTP请求的Header中,这带来一个问题,在接口开发的时候必须每个接口进行版本判断,来确定到底调哪个版本的实现;给开发工作带了了极大的不便,但是设计又没办法改,只能改实现方式。最后经过研究spring mvc源码发现,可以通过修改spring mvc做URL匹配的方式来将头里面的version放到URL里面。
例如:
请求的URL是 /api/hello
Header是version: v1.1
Controller:
@RestController
@RequestMapping("/api/v1.1")
public class ApiController{
@GetMapping("/hello")
public String hello(String name) {
return "hello, " + name;
}
}
spring mvc默认情况会返回404
经过我们的自定义后可以正确请求到改controller里面对应的方法上
SpringMVC中控制请求和Controller中方法映射的一个重要类是RequestMappingHandlerMapping
,这个类里面通过UrlPathHelper
类来控制如何从Request对象中取路径的,我们想把版本拼接到路径中,只需要自定义UrlPathHelper
这个类,再将这个类注入到RequestMappingHandlerMapping
中就可以。
首先自定义一个UrlPathHelper
import org.springframework.util.StringUtils;
import org.springframework.web.util.UrlPathHelper;
import javax.servlet.http.HttpServletRequest;
public class CustomUrlPathHelper extends UrlPathHelper {
@Override
public String getLookupPathForRequest(HttpServletRequest request) {
String version = request.getHeader("version");
String path = this.getPathWithinApplication(request);
if (path.startsWith("/api") && StringUtils.hasText(version)) {
//当URL是api开头且头里面带了version则进行URL转换,否则不做处理
path = path.replace("/api", "/api/" + version);
}
return path;
}
}
自定义一个CustomRequestMappingHandlerMapping
继承RequestMappingHandlerMapping
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.UrlPathHelper;
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override
public UrlPathHelper getUrlPathHelper() {
// 当有请求过来时,spring通过这个方法来获取UrlPathHelper实例,我们返回自定义的实例
return new CustomUrlPathHelper();
}
}
自定义WebMvcConfig
来将我们自定义的配置注入到框架中
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new CustomRequestMappingHandlerMapping();
}
}