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. 基于ABP落地领域驱动设计-03.仓储和规约最佳实践和原则

    目录 系列文章 仓储 仓储的通用原则 仓储中不包含领域逻辑 规约 在实体中使用规约 在仓储中使用规约 组合规约 学习帮助 围绕DDD和ABP Framework两个核心技术,后面还会陆续发布核心构件实 ...

  2. Unity 异步加载 进度条

    当我们进行游戏开发时,时常会进行场景切换,如果下个场景较大,切换时就会出现卡顿现象,甚至看起来像是"死机",非常影响用户体验,我们这时就可以运用异步加载,在界面上显示加载的进度条以 ...

  3. 模拟windows10计算器的实现

    用户界面部分: import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.HashMap; impo ...

  4. POJ 3347 Kadj Squares 计算几何

    求出正方形的左右端点,再判断是否覆盖 #include <iostream> #include <cstdio> #include <cstring> #inclu ...

  5. Tomcat 中文乱码,设置UTF-8

    1.修改tomcat的conf目录下 server.xml文件加上 URIEncoding="UTF-8" <Connector port="8080" ...

  6. ios多线程开发基础

    多线程编程:下载数据时,开辟子线程,减少阻塞时间,和主线程并发运行,提升用户体验 1.Thread 1>新建Thread对象,带一selector方法,调用start方法,开启子线程 2> ...

  7. netcore3.1 + vue (前后端分离)生成PDF(多pdf合并)返回前端打印

    1.使用Adobe Acrobat XI Pro编辑pdf模板 2.公共类代码 3.service层调用 4.Controller层 5.前端(Vue) 因为print.js不支持宋体,所以打算用后台 ...

  8. [刘阳Java]_处理并发有哪些方法

    1.HTML静态化 ,将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素2.禁止重复提交:用户提交之后按钮置灰,禁止重复提交3.用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取 ...

  9. File类与常用IO流第五章——IO字符流

    字符流,只能操作文本文件,不能操作图片.视频等非文本文件 字符输入流 java.io.Reader 字符输入流中一些共性的成员方法 int read():读取单个字符并返回. int read(cha ...

  10. 【LeetCode】137. 只出现一次的数字 II(剑指offer 56-II)

    137. 只出现一次的数字 II(剑指offer 56-II) 知识点:哈希表:位运算 题目描述 给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 .请你找出并返回 ...