1、基本使用
-
之前使用不同的url表示不同的操作,这些操作都只能使用get 或者 post两种请求方式,区分不同操作的取决于url,而不是请求方式!
-
例如:/getuser获取用户、/deluser删除用户、/moduser修改用户、/saveuser保存用户;
-
使用rest风格可以将同一个url用不同的请求方式区分,然后根据不同的请求方式执行不同的操作;也就是说针对同一个url,不同的请求方式做不同的处理。
-
例如:/userGET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
1.1 页面参数
-
页面中需要使用表单提交,且提交非get/post请求时需要提交一个隐藏域,name = “_method” value = “请求”。
-
首先表单中必须是method = post,然后提交后会解析隐藏域,将post请求转化为对应的请求value值。
<h1>测试rest风格: </h1>
<form action="/user" method="get">
<input type="submit" value="get-提交">
</form>
<form action="/user" method="post">
<input type="submit" value="post-提交">
</form>
<form action="/user" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="put-提交">
</form>
<form action="/user" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="delete-提交">
</form>
1.2 控制层
-
控制层所有的path路径全都是一样的,但是需要给@RequestMapping注解设置属性处理的方式。
-
@ResponseBody表示直接响应到页面中不经过视图处理器。
@Controller
public class RequestController {
@RequestMapping(value = "/user", method = RequestMethod.GET)
@ResponseBody
public String getUser(){
return "张三 -> get";
}
@RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody
public String postUser(){
return "张三 -> post";
}
@RequestMapping(value = "/user", method = RequestMethod.PUT)
@ResponseBody
public String putUser(){
return "张三 -> put";
}
@RequestMapping(value = "/user", method = RequestMethod.DELETE)
@ResponseBody
public String deleteUser(){
return "张三 -> delete";
}
}
1.3 application.yaml配置
-
开启rest风格的允许,否则依旧是不生效的。
-
这几个步骤缺少其中的任何一个rest风格都不会生效。
spring:
mvc:
hiddenmethod:
filter:
enabled: true
2、rest映射原理
-
rest映射主要是通过post请求提交并且携带隐藏域(_method),然后由HiddenHttpMethodFilter类将其转换。
-
SpringBoot启动就自动装配WebMvcAutoConfiguration类,然后由该类进行条件装配OrderedHiddenHttpMethodFilter类。
2.1 OrderedHiddenHttpMethodFilter解析
-
该类继承自HiddenHttpMethodFilter类,主要是绑定spring.mvc.hiddenmethod.filter配置参数
-
而这里有个参数name = enabled,并且默认是false的也就是说默认并没有开启rest映射。
public class WebMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class) //IOC中是否已经有这个组件
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled")
//绑定spring.mvc.hiddenmethod.filter配置文件,并且默认情况下是不开启rest
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
}
2.2 HiddenHttpMethodFilter解析
-
OrderedHiddenHttpMethodFilter类继承自HiddenHttpMethodFilter类。
-
HiddenHttpMethodFilter类首先是以过滤器的形式对请求进行过滤拦截,当请求过来被HiddenHttpMethodFilter拦截。
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
//允许的请求
private static final List<String> ALLOWED_METHODS =
Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
public static final String DEFAULT_METHOD_PARAM = "_method"; //固定的请求与name名
private String methodParam = DEFAULT_METHOD_PARAM; //可以对这个进行更改
public void setMethodParam(String methodParam) { //判断合法性
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
// 核心方法
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter(requestToUse, response);
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
@Override
public String getMethod() {
return this.method;
}
}
}
核心方法doFilterInternal解析如下:
-
首先这里必须是post请求,并且请求是合法的!
-
然后获取到methodParam隐藏域的值,判断合法性
-
不缺分请求方式值的大小写,并且全部转为大写。
-
判断提交的请求是否是delete、put、PATCH请求。
-
最后由HttpMethodRequestWrapper包装器解析并且返回,返回的值就是真正的请求方式!
3、修改默认的参数
由于无法直接修改绑定的配置参数,进而无法将methodParam = _method改成指定的名称。但是可以根据SpringBoot的条件装配进行装配。
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled")
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
可以看到这个类的装配只有IOC中没有HiddenHttpMethodFilter类才会装配,那么我们可以手动向容器中注入这个Bean,并且顺手指定methodParam值。
@Configuration
public class WebConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
hiddenHttpMethodFilter.setMethodParam("query");
return hiddenHttpMethodFilter;
}
}
-
由于手动向IOC中丢入HiddenHttpMethodFilter类,那么条件装配将不会生效。
-
且前端传入的隐藏参数名称也要改为指定的query进行转换才能使得rest生效。
<input type="hidden" name="query" value="转换请求类型">