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. docker创建和使用mysql

    container和image是两种不同的概念,image即指存在的镜像,container指docker运行起来后image的实例. 当使用docker kill 把某个正在运行的实例kill掉之后 ...

  2. 简易版JDBC连接池

    JDBC连接池mini版的实现 首先是工具类 DbUtil 主要参数就是Driver.User.PWD等啦,主要用于建立连接 URL需要注意的是SSL和serverTimezone参数,和mysql驱 ...

  3. 循序渐进BootstrapVue,开发公司门户网站(5)--- 使用实际数据接口代替本地Mock数据

    在我们开发一些门户网站功能的时候,有时候我们需要快速的创建数据模型来进行数据展示,因为数据结构可能处于不断的修正变化之中,因此服务端的接口我们可以暂时不开发,当我们基本完成数据结构和界面展示的时候,就 ...

  4. 【LeetCode每日一题 Day 2】2. 两数相加

    大家好,我是编程熊,今天是LeetCode每日一题的第二天,一起学习的是LeetCode第二题<两数相加>. 题意 给你两个 非空 的链表,表示两个非负的整数.它们每位数字都是按照 逆序 ...

  5. SQL 清除数据库日志

    exec sp_detach_db 'MCS4WLSQM','true' --运行上一个命令. --改名LOG文件后再关闭上一行代码,然后再运行下一行代码 exec sp_attach_single_ ...

  6. Gym 101334A Area 51 数学

    大致题意: 给出n个建筑的二维坐标,每个建筑名称为一个字母,不同坐标的建筑可以有同一名称,并保证这些坐标都是在y轴上半轴.给出一串建筑名称的字符串,在X轴上找出一个或多个区间,使Nick在这个区间上从 ...

  7. Redis启动正常,一段时间后报错,连不上redis

    Redis报错 1.redis在最终目标上移动临时数据库文件时出错 错误:redis:Error moving temp DB file temp-13792.rdb on the final des ...

  8. Linux系统下安装MongoDB

    下载安装包 去MongoDB官网https://www.mongodb.com/try/download/community,下载社区版的安装包: 我的Linux系统是CentOS 7.5版本的,通常 ...

  9. Docker Swarm介绍

    1 什么是swarmSwarm是Docker公司推出的docker集群管理平台,它将一群Docker主机变成了一台单一的虚拟Docker主机.Swarm使用标准的Docker API接口,因此,任何支 ...

  10. 「万字图文」史上最姨母级Java继承详解

    摘要:继承是面向对象软件技术中的一个概念.它使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用. 本文分享自华为云社区<「万字图文」史上最姨母级Java继承详解丨[奔跑吧!JAVA] ...