在此之前首先要将一下为什么需要使用HiddenHttpMethodFilter:
Ⅰ、 因为我们在开发中已经规范了一种风格就是MVC的REST风格,它拥有四种请求,分别是:GET、POST、PUT、DELETE。分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
Ⅱ、通常的浏览器却只支持get和post请求,这个时候就需要用到我们的HiddenHttpMethodFilter 过滤器来将post请求转换为put跟delete请求。
HiddenHttpMethodFilter源码:(上来看见源码就头大话,可以直接跳过这直接看第一点即可,一步一步讲解的,如有错误欢迎大牛们指正)
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
private static final List<String> ALLOWED_METHODS;
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = "_method";
public HiddenHttpMethodFilter() {
}
public void setMethodParam(String methodParam) {
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter((ServletRequest)requestToUse, response);
}
static {
ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
public String getMethod() {
return this.method;
}
}
}
- 我们首先要知道它是一个Filter,与CharacterEncodingFilter一样继承了OncePerFilter,所以我们最重要的就是关注的它的doFilterInternal方法
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter((ServletRequest)requestToUse, response);
}
-
我们首先关注第二行的
HttpServletRequest requestToUse = request;
新建requestToUse是为了替换原有的request做准备。 -
条件:
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null)
进入条件是方法必须为"post",后面一个条件为无异常。 -
获取请求方法:
String paramValue = request.getParameter(this.methodParam);
通过此行代码获取到请求的方式,如:PUT、DELETE、PATCH。方式是通过自己set方法设置:public void setMethodParam(String methodParam) { Assert.hasText(methodParam, "'methodParam' must not be empty"); this.methodParam = methodParam; }
-
替换请求方法:
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
① 如果method不为null,就将method参数全部转成大写。String method = paramValue.toUpperCase(Locale.ENGLISH);
② 接着判断是否是ALLOWED_METHODS(PUT、DELETE、PATCH,可以去看全部源码静态代码块中的内容就是其值)常量其中一种:ALLOWED_METHODS.contains(method)
③ 如果前两者条件都满足了就开始替换request请求。
a.调用自己的内部类(HttpMethodRequestWrapper)的构造器,将request和method传入,通过反射的方式获取其method:requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
b.因为内部类(HttpMethodRequestWrapper)重写了getMethod方法,返回的是你设置传入的method(就一个字:妙)
public String getMethod() { return this.method; }
c.此时requestToUse已经是你设置的request方法了(PUT、DELETE、PATCH)
- 放行:
filterChain.doFilter((ServletRequest)requestToUse, response);
将新的requestToUse代替原有的那个request执行后面的代码。