概述
该过滤器的作用是读取POST
表单中表示客户端真正想用的HTTP method
的隐藏字段(缺省情况下是_method
),将其值设置到请求的method
属性,也就是随后通过HttpServletRequest#getMethod()
获取的值是POST
表单中_method
字段的值,而不再是POST
。
之所以会有这种操作,原因是浏览器通常只支持GET
,POST
,所以一些javascript
库,比如Prototype
,就采用该方法传递一些真正想用的HTTP method
,比如DELETE
,PUT
和PATCH
(实际上也最多支持这三种HTTP method
)。
缺省情况下,POST
表单中表示客户真正想用的HTTP method
的字段名称为_method
,但实际上也可以是其他名称,该过滤器也缺省使用此名称。但如果用了其他值,可以通过setMethodParam(String)
告知该过滤器。
注意,文件上传总是使用
POST
,并且相应的过滤器MultipartFilter
处理会检查POST
请求的主体(body
)参数,所以该过滤器HiddenHttpMethodFilter
必须在文件上传处理过滤器(针对multipart
)之后被调用。
该过滤器继承自OncePerRequestFilter
,也就是说,它在整个请求处理过程中最多只会被应用一次。
Springboot
提供了一个OrderedHiddenHttpMethodFilter
继承自HiddenHttpMethodFilter
应用在基于Springboot
的Servlet Web
应用中。OrderedHiddenHttpMethodFilter
在HiddenHttpMethodFilter
的功能上增加了接口OrderedFilter
定义的过滤器顺序,并且缺省使用优先级-10000
。在整个Servlet
过滤器链中,过滤器的顺序数字越小,表示越先被调用。
源代码分析
package org.springframework.web.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
// 真正支持可替换的HTTP method的集合 :PUT, DELETE, PATCH
private static final List<String> ALLOWED_METHODS =
Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
/** Default method parameter: _method. */
// 从POST FORM中获取真正HTTP method的字段的名称的缺省值:"_method"
public static final String DEFAULT_METHOD_PARAM = "_method";
// 从POST FORM中获取真正HTTP method的字段的名称,缺省使用"_method"
private String methodParam = DEFAULT_METHOD_PARAM;
/**
* Set the parameter name to look for HTTP methods.
* 设置从POST FORM中获取真正HTTP method的字段的名称
* @see #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) {
// 如果当前 request.getMethod()是POST并且尚无错误,则获取POST FORM的_method参数,
// 如果它存在并且是允许的HTTP method之一,则将当前请求进行包装,使其在调用getMethod()
// 时返回请求参数_method的值
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);
}
// 对请求做封装,使其使用方法 getMethod()返回指定的值
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;
}
}
}