SpringMVC框架如何“放行”客户端发起的PUT/DELETE/PATCH请求-源码分析

SpringMVC框架如何放行客户端发起的PUT/DELETE/PATCH请求

我们在使用浏览器查询 ES 数据的时候,客户端可以选择GET PUT POST DELETE等请求方式, 而我们知道,HTTP 只支持GET和POST类型的请求, 那如何实现 PUT 和 DELETE 呢?

看看 SpringMVC 官方的说法:
找到官方文档中的标题为 “HTTP Method Conversion”位置,我们看到会有以下描述:

While HTTP defines these four methods, HTML only supports two: GET and
POST. Fortunately, there are two possible workarounds: you can either
use JavaScript to do your PUT or DELETE, or you can do a POST with the
“real” method as an additional parameter (modeled as a hidden input
field in an HTML form). Spring’s HiddenHttpMethodFilter uses this
latter trick. This filter is a plain Servlet filter and, therefore, it
can be used in combination with any web framework (not just Spring
MVC). Add this filter to your web.xml, and a POST with a hidden method
parameter is converted into the corresponding HTTP method request.

重要的看这句话:

Add this filter to your web.xml, and a POST with a hidden method
parameter is converted into the corresponding HTTP method request.

也就是需要两步:

  1. 在web.xml中配置过滤器 HiddenHttpMethodFilter
  2. 添加一个隐藏域参数

(not just Spring MVC) 所有的web请求都可以这么做,具体就是将post请求,通过过滤器,转换为HTTP不支持的PUT和DELETE请求。

先来分析下源码,这个 HiddenHttpMethodFilter 到底干了啥!

既然是过滤器,那么主要看 FilterChain 对象,是怎么执行 doFilter 方法的

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);
}

单看源码,猜测如果是POST,应该会修改请求的方式
看一下这行代码做了什么事情

String paramValue = request.getParameter(this.methodParam);

该类中有个属性:

private String methodParam = "_method";

那么paramValue 的值,就是我们请求中 参数的key为_method的value的值。也就是 请求参数 中会有 _method=xxx

再看 if (ALLOWED_METHODS.contains(method)) , ALLOWED_METHODS是什么?

本类中有一个静态的代码块, 也就是 类加载的时候,就执行静态代码块中的内容:

static {
    
    
    ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
}

这里的 HttpMethod 是一个枚举类

public enum HttpMethod {
    
    
    GET,
    HEAD,
    POST,
    PUT,
    PATCH,
    DELETE,
    OPTIONS,
    TRACE;

那么 ALLOWED_METHODS 常量在类加载完成之后就已经初始化为包含 “PUT”,“DELETE”,“PATCH” 三个字符串的List集合了。

最后通过 requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method); 将method修改为 隐藏域( _method=xxx)中配置的 xxx。

我们写一段代码调试一下,看是不是与以上分析的一致:

  1. html
<form th:action="@{/order}" th:method="post">
    <input type="hidden" value="put" name="_method">
    name:<input type="text" name="name" value="name1"><br>
    <input type="submit" value="修改订单">
</form>
  1. SpringMVC控制器方法
@RequestMapping(value = "/order", method = RequestMethod.PUT)
public String updateOrder(){
    
    
    System.out.println("修改订单");
    return "succ";
}
  1. web.xml 配置过滤器
<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. 调试运行
    在这里插入图片描述
    通过过滤器,将POST请求,转换为PUT请求。我觉得Spring官方说的 “not just Spring MVC” 很重要,如果哪天,你也自己实现框架或者开源软件,你也可以这么做!

猜你喜欢

转载自blog.csdn.net/asdfjklingok/article/details/119880021