Spring Boot核心技术之Rest映射以及源码的分析

该博客主要是Rest映射以及源码的分析,主要是思路的学习。SpringBoot版本:2.4.9

环境的搭建

主要分两部分:

Index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<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="delete">
<input value="REST-DELETE 提交" type="submit"/>
</form> <form action="/user" method="put">
<input value="REST-PUT提交"type="submit" />
</form> </body>
</html>

controller

package com.xbhong.Controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*; @RestController
public class myContro {
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
} // @PostMapping("/user")
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
} @RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
} @RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}
}

测试功能是否可用:

观察效果:

可以看到最后两个请求都变成Get的请求了。由此我们可以引出来如下问题:

  1. 为什么不同的请求会出现相同的结果
  2. 怎么实现的才能完成我们的功能

后面的文章就是围绕上述的问题进行展开的。

解决问题:

像之前的SpringMVC中的表单请求通过HiddenHttpMethodFilter实现的,这样我们查一下在SpringBoot中是怎么样的。

默认双击Shift键,输入WebMvcAutoConfiguration这个类

找到默认配置的过滤器:

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

通过OrderedHiddenHttpMethodFilter进入父类HiddenHttpMethodFilter:

在使用之前,我们需要将后两个的请求方式改写成post方式,并且需要在请求的时候传入一个_method方法(设置隐藏域);

<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>

为什么要这样设置呢?我们到HiddenHttpMethodFilter中看下。

流程:

  1. 第一步保存了传入的请求
  2. 当该请求时post,并且请求没有异常,才能进入下面方法,不是Post请求将直接通过过滤器链放行。
  3. 获取请求中的参数(this.methodParam)
  4. DEFAULT_METHOD_PARAM = _method获得(作为真正的请求方式)

然后再测试,发现还是没有实现。

我再回到WebMvcConfiguration中:

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

首先该类存在于容器中。

@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)

当容器中不存在HiddenHttpMethodFilter这个类的时候,下面内容开启(条件装配);

@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)

表示:绑定的配置文件中:spring.mvc.hiddenmethod.filter名字为enable是默认不开启的(后续版本可能开启)。这样我们就找到了问题的所在。

所以我们需要在配置文件中配置(两种方法都可以)。

yaml:

spring:
mvc:
hiddenmethod:
filter:
enabled: true

properties:

spring.mvc.hiddenmethod.filter.enabled=true

重启项目:成功解决。

源码分析:

主要是分析doFilterInternal:

@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);
}
  • 表单提交会带上_method=PUT

  • 请求过来被HiddenHttpMethodFilter拦截

  • 请求是否正常,并且是POST

  • 获取到_method的值。

  • 兼容以下请求;PUT.DELETE.PATCH

    当方法走到上述代码11行时,进入ALLOWED_METHODS:

    private static final List<String> ALLOWED_METHODS =
    Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
    HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));

    如果请求里的参数在ALLOWED_METHODS存在,则执行下面代码。

  • 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。

    进入HttpMethodRequestWrapper对象中,向上找父类。本质还是HttpServletRequest

    由下面代码:

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

    可知,接收到前面的请求封装为HttpMethodRequestWrapper返回。

  • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。

部分补充:

由于源码中规则:将获得请求中的参数无条件的以英文格式转完成大写,所以前端的value="PUT"value的值大小写无影响。

String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);

Rest使用客户端工具,

  • 如PostMan直接发送Put、delete等方式请求,无需Filter。

参考文献:

B站尚硅谷

结束:

如果你看到这里或者正好对你有所帮助,希望能点个关注或者推荐,感谢;

有错误的地方,欢迎在评论指出,作者看到会进行修改。

Spring Boot核心技术之Rest映射以及源码的分析的更多相关文章

  1. 快速开发架构Spring Boot 从入门到精通 附源码

    导读 篇幅较长,干货十足,阅读需花费点时间.珍惜原创,转载请注明出处,谢谢! Spring Boot基础 Spring Boot简介 Spring Boot是由Pivotal团队提供的全新框架,其设计 ...

  2. Maven 搭建spring boot多模块项目(附源码),亲测可以,感谢原创

    原创地址:https://segmentfault.com/a/1190000005020589 我的DEMO码云地址,持续添加新功能: https://gitee.com/itbase/Spring ...

  3. Spring Boot 整合单机websocket(附github源码)

    websocket 概念 websocket 是一个通信协议,通过单个 TCP 连接提供全双工通信.websocket 连接成功后,服务端和客户可以进行双向通信.不同于 http 通信协议需要每次由客 ...

  4. Spring之SpringMVC前端控制器DispatcherServlet(源码)分析

    1.DispatcherServlet作用说明 DispatcherServlet提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得 ...

  5. spring事务详解(三)源码详解

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  6. 微服务架构 | *2.3 Spring Cloud 启动及加载配置文件源码分析(以 Nacos 为例)

    目录 前言 1. Spring Cloud 什么时候加载配置文件 2. 准备 Environment 配置环境 2.1 配置 Environment 环境 SpringApplication.prep ...

  7. Spring中AOP相关的API及源码解析

    Spring中AOP相关的API及源码解析 本系列文章: 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configuration注解? 谈谈Spring ...

  8. Spring Ioc源码分析系列--Ioc源码入口分析

    Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...

  9. DispatcherServlet源码注解分析

    DispatcherServlet的介绍与工作流程 DispatcherServlet是SpringMVC的前端分发控制器,用于处理客户端请求,然后交给对应的handler进行处理,返回对应的模型和视 ...

随机推荐

  1. Arduino参考手册-函数和变量及电路图

    标题: Arduino参考手册-函数和变量及电路图 作者: 梦幻之心星 sky-seeker@qq.com 标签: [#Arduino,#参考手册,#函数,#变量] 目录: [Arduino] 日期: ...

  2. 十八、.net core(.NET 6)搭建ElasticSearch(ES)系列之使用Logstash通过Rabbitmq接收Serilog日志到ES

    使用Logstash通过Rabbitmq接收Serilog日志到ES 首先,要部署logstash 为了与前面的ElasticSearch版本保持一致,此处Logstash下载的版本也是7.13.1, ...

  3. 高性能的Redis数据结构小结

    一.概述 Redis 作为一种 KV 缓存服务器,有着极高的性能,相对于 Memcache,Redis 支持更多种数据类型,因此在业界应用广泛. 记得刚毕业那会参加面试,面试官会问我 Redis 为什 ...

  4. 01-ubuntu18.04安装docker脚本[含自动配置apt]

    01-ubuntu18.04安装docker脚本[含自动配置apt] 脚本一键安装docker,并配置阿里云的docker镜像加速. #!/bin/bash #更新apt源为清华源 echo &quo ...

  5. Kubernetes自动伸缩pod-HPA

    在运维中,虽然能预先知道负载何时会飙升,或者如果负载的变化是较长时间内逐渐发生的,手动扩容也是可以接受的,但指望靠人工干预来处理突发而不可预测的流量增长,仍然不够理想. 幸运的是,Kubernetes ...

  6. Linux系统安装-C7

    1.安装部署操作系统 (1)创建虚拟机,加载系统镜像 (2)进入系统引导界面进行配置 补充:centos7系统网卡名称 默认系统的网卡名称为 eth0 eth1 –centos6 默认系统的网卡名称为 ...

  7. 39、升级linux的内核

    39.1.什么是linux系统内核: 操作系统是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件.一个计算机 系统是一个硬件和软件的共生体,它们互相依赖,不可分割.计算机的硬件,含有外 ...

  8. [JLOI2011]飞行路线题解

    题目描述 Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司.该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并且航线有一定的 ...

  9. 运行cmd时提示你可能没有适当的权限访问该项目

    Windows无法访问指定设备.路径或文件.你可能没有适当的权限访问该项目.   方法/步骤   在C:\Windows\System32目录下中找到cmd.exe文件   右键点击 "属性 ...

  10. POJ 1775 Sum of Factorials 数论,基础题

    输入一个小于1000000的正整数,是否能表达成式子:a1!+a2!+a3!+...+an (a1~an互不相等). 因为10!>1000000,所以先打1~10的阶乘表.从a[10]开始递减判 ...