SpringBoot - Web参数处理
探讨SpringBoot接收和处理参数的方式。
1. 注解方式
SpringMVC为我们提供了许多注解来接收参数。
1.1 常用注解
SpringMVC常用的9个参数绑定注解:
@RequestParam
接收请求中的参数;@RequestBody
接收请求体内容;@RequestHeader
接收请求头内容;@RequestAttribute
接收Request域中的参数;@PathVariable
接收请求路径中的路径变量;@CookieValue
接收Cookie中的参数;@SessionAttributes
接收Session域中的参数;@ModelAttribute
接收隐含模型中的参数;@MatrixVariable
接收请求路径中的矩阵变量。
1.2 @MatrixVariable
其他注解在学习SpringMVC时就已经了解过了,现在单独学习@MatrixVariable。
1.2.1 矩阵变量
一道经典的面试题:
现在Cookie被禁用了,如何获取Session域中的内容?
由于服务器是按照Cookie中的一个参数JSESSIONID(初次访问服务器才会有)来查找Session对象,
从而获取Session域中的内容。现在Cookie被禁用,服务器无法获取JSESSIONID,
所以也就无法获取Session域的内容。
我们可以使用URL重写的方式让URL携带JSESSIONID:
localhost:8080/example;JSESSIONID=ghco9xdnaco31gmafukxchph
使用这种方式传递的参数就叫矩阵变量,可以使用@MatrixVariable注解接收并绑定。
1.2.2 矩阵变量的语法
在路径变量后面加分号后写在后面,多个矩阵变量用分号隔开,
可以在任何路径变量后加:
localhost:8080/example;id=1/example;id=2;name=张三
1.2.3 矩阵变量的类型
- 单个类型,不可再分的类型(如数字,字符串等):
localhost:8080/example;name=张三;sex=男
- 集合类型(List):
第一种写法:变量值可以写在一起,多个值用逗号隔开:
localhost:8080/example;sex=男,女
第二种写法:变量值不写在一起,多个值用分号隔开:
localhost:8080/example;sex=男;sex=女
1.2.4 作用
注解@MatrixVariable用来接收请求路径中的矩阵变量。
1.2.5 作用范围
- 方法参数(ElementType.PARAMETER)
1.2.6 属性
- String name/value:指定接收的矩阵变量的名称;
- String pathVar:指定矩阵变量所在的路径变量:
当不同的路径变量位置的矩阵变量同名时,可以指定此值来作为区分; - boolean required:指定该变量不存在时是否抛出异常;
- String defaultValue:指定该变量不存在时的默认值。
1.2.7 使用方式(原理)
SpringBoot默认禁用了矩阵变量,原理分析:
在SpringBoot对WebMVC的自动配置类中:
// org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
// WebMvcAutoConfiguration.java Line:179~322
// ... 省略其他代码
// 此类为WebMvcAutoConfiguration自动配置类的内部类,为WebMVC的配置类,实现了WebMvcConfigurer接口。
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
// ... 省略其他代码
// 此方法配置了路径匹配规则
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
if (this.mvcProperties.getPathmatch()
.getMatchingStrategy() == WebMvcProperties.MatchingStrategy.PATH_PATTERN_PARSER) {
configurer.setPatternParser(new PathPatternParser());
}
configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());
configurer.setUseRegisteredSuffixPatternMatch(
this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {
String servletUrlMapping = dispatcherPath.getServletUrlMapping();
if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {
// 使用UrlPathHelper帮助解析请求路径
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setAlwaysUseFullPath(true);
configurer.setUrlPathHelper(urlPathHelper);
}
});
}
// ... 省略其他代码
}
SpringMVC是使用了UrlPathHelper
来帮助解析请求路径的,
对矩阵变量的处理如下:
// org.springframework.web.util.UrlPathHelper
// UrlPathHelper.java
public class UrlPathHelper {
// ... 省略其他代码
// 是否移除分号内容(矩阵变量)
private boolean removeSemicolonContent = true;
// 是否移除矩阵变量的Setter
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
checkReadOnly();
this.removeSemicolonContent = removeSemicolonContent;
}
// 获取是否应该移除矩阵变量
public boolean shouldRemoveSemicolonContent() {
checkReadOnly();
return this.removeSemicolonContent;
}
// 过滤URI
private String decodeAndCleanUriString(HttpServletRequest request, String uri) {
// 此处将矩阵变量从URI中移除
uri = removeSemicolonContent(uri);
uri = decodeRequestString(request, uri);
uri = getSanitizedPath(uri);
return uri;
}
// 移除矩阵变量
public String removeSemicolonContent(String requestUri) {
// 判断是否需要移除矩阵变量,如果是那就移除,如果不是仅移除JSESSIONID
return (this.removeSemicolonContent ?
removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri));
}
// 移除矩阵变量的具体过程,这里就不在分析
private static String removeSemicolonContentInternal(String requestUri) {
int semicolonIndex = requestUri.indexOf(';');
if (semicolonIndex == -1) {
return requestUri;
}
StringBuilder sb = new StringBuilder(requestUri);
while (semicolonIndex != -1) {
int slashIndex = sb.indexOf("/", semicolonIndex + 1);
if (slashIndex == -1) {
return sb.substring(0, semicolonIndex);
}
sb.delete(semicolonIndex, slashIndex);
semicolonIndex = sb.indexOf(";", semicolonIndex);
}
return sb.toString();
}
// ... 省略其他代码
}
可以看到UrlPathHelper是默认移除分号后的内容的,即禁用了矩阵变量。
我们可以定制化SpringBoot来启用矩阵变量。有两种定制化方式:
- 在容器中添加一个WebMvcConfigurer接口的实现类(可以使用匿名内部类),
重写configurePathMatch方法并设置UrlPathHelper不移除分号内容:
@Bean
public WebMvcConfigurer matrixVariableEnabledWebConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 设置启用矩阵变量
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
};
}
- 编写一个配置类并实现WebMvcConfigurer接口,
重写configurePathMatch方法并设置UrlPathHelper不移除分号内容:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 设置启用矩阵变量
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
启用了矩阵变量之后就可以使用@MatrixVariable编写处理器了:
// 矩阵变量需要绑定一个路径变量
@RequestMapping("/example/{param}")
@ResponseBody
public Object matrixVariable(
@PathVariable("param") String param,
@MatrixVariable("id") Integer id,
@MatrixVariable("value") List<String