美文网首页
HTML
只支持 GET 和 POST!

HTML 只支持 GET 和 POST!

作者: AlphaHinex | 来源:发表于2021-08-22 11:08 被阅读0次

    原文地址:https://alphahinex.github.io/2021/08/22/hidden-http-method-filter/

    cover

    description: "这很不 RESTful"
    date: 2021.08.22 10:26
    categories:
    - Spring
    tags: [HTML, Spring]
    keywords: RESTful, HTML, form, method, get, post, delete, put, HiddenHttpMethodFilter, MultipartFilter


    HTML form method

    在 Web 开发中,常规的提交数据方式为使用 form 表单,例如:

    <form action="" method="get" class="form-example">
      <div class="form-example">
        <label for="name">Enter your name: </label>
        <input type="text" name="name" id="name" required>
      </div>
      <div class="form-example">
        <label for="email">Enter your email: </label>
        <input type="email" name="email" id="email" required>
      </div>
      <div class="form-example">
        <input type="submit" value="Subscribe!">
      </div>
    </form>
    

    form 中各属性的值,可参考 MDN 文档,其中,method 属性支持的值如下:

    method

    The HTTP method to submit the form with. Possible (case insensitive) values:

    • post: The POST method; form data sent as the request body.
    • get: The GET method; form data appended to the action URL with a ? separator. Use this method when the form has no side-effects.
    • dialog: When the form is inside a <dialog>, closes the dialog on submission.

    这在 JSP 时代通常只使用 getpost 两种 method 时并无大碍,但在 RESTful 盛行的今日,无疑显得很不和谐。

    Spring 的解决方式

    在 Spring 提供的 form 标签库 中,对 HTML 中的 form 进行了全面的丰富,在 JSP 页面中引入 taglib:

    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    

    之后可以通过 <form:form> 的方式使用,如:

    <form:form method="delete">
        <p class="submit"><input type="submit" value="Delete Pet"/></p>
    </form:form>
    

    <form:form> 最终也会被服务端渲染为 <form>,那为什么这里的 method 不再局限于 HTML 规范中要求的值呢?

    HiddenHttpMethodFilter

    在 Spring 中,使用了一种 HTTP Method 转换方式:以上面代码片段为例,实际提交的表单的 method 是 POST 类型的,同时在请求参数中包含了一个隐藏的属性,默认为 _method,里面存放了真正的 method —— DELETE

    在服务端,接收请求时,有一个 HiddenHttpMethodFilter,负责将请求的 method 修改为隐藏属性中的实际 method,再传入后续处理流程,以使得我们的 Controller 能够使用 @DeleteMapping 接收到请求。

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

    一切都还挺美好的,直到……

    上传文件为空

    Spring 中,需要上传文件时,又同时使用了 HiddenHttpMethodFilter,如果配置不当,大概率会遇到 Controller 中接到的 MultipartFile 为空的情况。在 HiddenHttpMethodFilter源码 中,其实已经说明了这个问题:

    NOTE: This filter needs to run after multipart processing in case of a multipart POST request, due to its inherent need for checking a POST body parameter. So typically, put a Spring org.springframework.web.multipart.support.MultipartFilter before this HiddenHttpMethodFilter in your web.xml filter chain.

    只需要让处理上传文件的 Filter 先于处理 HTTP method 隐藏属性的 Filter 即可。

    相关文章

      网友评论

          本文标题:HTML 只支持 GET 和 POST!

          本文链接:https://www.haomeiwen.com/subject/ebsfiltx.html