自学笔记记录:Spring Boot 2.5.0源码学习【3、Rest映射及源码解析】

【“没有什么是一个断点不能解决的”系列】

一、Rest映射及源码解析

1、Rest风格支持(使用 HTTP 请求方式动词来表示对资源的操作)

  • 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户

  • 现在:/user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户

  • 核心 Filter;HiddenHttpMethodFilter。

    • 用法:表单 method=post,隐藏域 _method=put。

    • 在 WebMvcAutoConfiguration.class 功能自动配置类中我们可以看到已经配了一个 OrderedHiddenHttpMethodFilter,默认我们的 Rest 功能是可以使用的,跟踪进入 OrderedHiddenHttpMethodFilter,发现继承 HiddenHttpMethodFilter,再跟踪进入 HiddenHttpMethodFilter,在 HiddenHttpMethodFilter 中规定,需要我们带一个隐藏的参数项"_method"。

      <form action="/user" method="get">
          <input value="REST-GET 提交" type="submit"/>
      </form>
      <form action="/user" method="post">
          <input value="REST-POST 提交" type="submit"/>
      </form>
      <form action="/user" method="post">
          <input name="_method" type="hidden" value="DELETE"/>
          <input value="REST-DELETE 提交" type="submit"/>
      </form>
      <form action="/user" method="post">
          <input name="_method" type="hidden" value="PUT"/>
          <input value="REST-PUT 提交" type="submit"/>
      </form>
      
    • 再回到 WebMvcAutoConfiguration.class 中的 OrderedHiddenHttpMethodFilter,发现它是与 spring.mvc.hiddenmethod.filter 配置属性值绑定的,并且默认是 false,也就是默认Rest功能是不开启的,因此我们需要设置为 true 才能开启 Rest 功能。

      @Bean
      @ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
      @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter",name = {"enabled"})
      public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
          return new OrderedHiddenHttpMethodFilter();
      }
      

2、Rest原理(该原理基于表单提交使用Rest的时候)

  • 表单提交会带上"_method"参数,_method=DELETE。

  • 请求过来会被 HiddenHttpMethodFilter 过滤器拦截,详细关注 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);
    }
    
    • 首先获取到原生的请求 requestToUse,然后判断原生请求的请求方式是否为 POST,以及判断原生请求的 Attribute 中是否有请求获取异常,判断条件成立(请求获取正常无异常,请求方式为 POST)后才能进入方法执行。

      1. 调用 request.getParameter(this.methodParam) 获取请求参数值,也就是获取参数"_method"的值 Value(来自于表单);

      2. 随后就是判断 paramValue 请求参数值是否获取到了,如果获取到了,就统一转为英文大写,因此在表单不区分大小写;

      3. 再来判断 ALLOWED_METHODS 允许的方法是否包含了我们参数值代表的方法,分别支持 PUTDELETEPATCH

        static {
            ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
                                                                         HttpMethod.DELETE.name(),
                                                                         HttpMethod.PATCH.name()));
        }
        
      4. 原生 Request(POST),包装模式 RequestWrapper 重写了 getMethod() 方法,返回的是传入的值(DELETE)

        关键步在于调用 new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method),利用包装模式 HttpMethodRequestWrapper,继承了 HttpServletRequestWrapper 类 也实现了 HttpServletRequest 接口,所以也是一个原生的 Request,但是又在 HttpMethodRequestWrapper 中重写了getMethod 方法,并且将要返回的 method 设置成了传进来的新的 method 值(也就是我们上面获取的参数值 DELETE,来自于表单)。

        也就是以后方法调用getMethod是调用RequestWrapper 中的getMethod方法,传入的是"_method"的值。

      5. 过滤器链放行的时候用 wrapper 放入执行。

3、如果使用客户端工具进行Rest请求访问

如 PostMan 工具直接发送 PUT、DELETE等方式请求,直接在 HTTP 层将请求方式改为了 PUT、DELETE,就不是 POST 了,判断就不会进入,也就不需要进行 Filter,因此也就不需要设置 spring.mvc.hiddenmethod.filter。

4、扩展

@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter",name = {"enabled"})
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
    return new OrderedHiddenHttpMethodFilter();
}

@ConditionalOnMissingBean 表示如果容器中没有 HiddenHttpMethodFilter 类型的组件,就会用默认的这个 OrderedHiddenHttpMethodFilter,如果我们不想用 OrderedHiddenHttpMethodFilter 中的一些规则(例如参数名字为"_method",我们想换成别的名字),我们就可以自己重写一个 HiddenHttpMethodFilter 并放入容器。

@Configuration(proxyBeanMethods = false)
public class WebConfig {
    
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        methodFilter.setMethodParam("_m");
        return methodFilter;
    }
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值