Spring Boot 2学习笔记(4)——请求映射

Rest的使用与原理

我们以前写一个请求的时候总是会在@RequestMapping里面写上/getUser 获取用户 、 /deleteUser 删除用户 、 /updateUser 修改用户 、 /saveUser 保存用户这类的请求,但是现在SpringBoot为我们提供了另外的一种方式来进行这些操作。

springboot 使用HTTP请求方式动词来表示对资源的操作 现在我们只需要写一个 /userGET来获取用户 、 DELETE来删除用户 、 PUT来修改用户 、 POST来保存用户

在这里插入图片描述

使用HTML表单出现的问题

然后我们写一个表单来测试一下到底能不能行

<h1>欢迎您</h1>
测试REST风格;
<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>

在这里插入图片描述
这个时候如果我们点击DELETE提交,发现出来的结果居然是 GET-张三 !!!
在这里插入图片描述

这和我们预想的结果不一致,是什么原因导致了这个结果呢 ? 原因其实很简单,因为 HTML 表单还不支持 PUT 和 DELETE,它识别不了putdelete所以它就采用默认的get方式,因此需要对html页面代码进行修改,把它们改为post方式

<h1>欢迎您</h1>
测试REST风格;
<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 name="_m" 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>

这个时候你再去点击DELETE提交,会发现这次出现的是POST了,但依然不是我们想要的DELETE

在这里插入图片描述
这是什么原因造成的呢?让我们打开源码来一探究竟

表单提交要使用REST的原理

找到WebMvcAutoConfiguration这个类,找到下面这段代码

在这里插入图片描述
我们发现,请求过来之后会被HiddenHttpMethodFilter拦截,并且@ConditionalOnProperty这个注解中matchIfMissing这一个属性的默认值为false,这也就解释了为什么我们无法执行DELETE请求了。

在yaml配置文件中将其改为true后再次访问DELETE请求
在这里插入图片描述
在这里插入图片描述
现在就可以正常访问了

那为什么一定要在 form 表单的 method 中写 POST 而不是 GET 呢?

继续往下看源码,在 HiddenHttpMethodFilter 类的 doFilterInternal 中给了我们解释
在这里插入图片描述
这个方法用来判断请求是否正常,并且是否为POST,然后它才会去获取到_method的值,那它又是如何获取到method的值的呢?
在这里插入图片描述
在官方源码的注释中我们就可以知道,它用一个包装模式requesWrapper重写了getMethod方法,返回的是传入的值。因此它才能执行PUTDELETEPATCH这些请求


以上说法仅用于 form表单 提交的时候,其他情境比如直接发送Put、delete等方式请求,无需Filter,也就不需要页面表单的Rest功能了。

这也正是源码中将matchIfMissing设为false的原因,因为这项功能仅在某些场景使用,当你需要用到时将其设置为true即可。
在这里插入图片描述


此外,我们在使用rest的时候其实也并不需要写那么长的注解,例如

可以将
@RequestMapping(value = "/user",method = RequestMethod.GET)
简写为
@GetMapping( "/user")

事实上,当我们去查看@GetMapping这个注解的源码的时候,会发现它和前一种写法本质上是一样的,两者并无区别,只是这样写帮助我们简化了代码

在这里插入图片描述

请求映射原理

只要我们使用了请求,那必然就会要涉及到HttpServlet,我们现在就来看看这个实现的过程
在这里插入图片描述
我们每一层的简单介绍一下,HttpServlet这个类呢主要处理的就是doGet这些请求,到了FrameworkServlet这里呢就变成了doService,但是这里的doService方法是个抽象方法,还没有具体实现。也就是说 SpringMVC 把对doService的实现放在了下一层:DispatcherServlet

FrameworkServlet 部分源码:
在这里插入图片描述
DispatcherServlet 部分源码:(感兴趣的可以自己去看源码)
在这里插入图片描述
而在这一大段代码中,最有用的信息是doDispatch,让我们来看一下这个方法里面又有些什么呢?
在这里插入图片描述
方法里面很多行代码官方都写了相应的注释,事实上所有的请求都会经过doDispatch()这个方法

在这里插入图片描述
我们对这里打个断点,来看一下他具体的流程是怎么样的
在这里插入图片描述

启动调试后,我们再次去访问 index 页面的时候,我们发现它产生了 5 个 handlerMapping

在这里插入图片描述
其中第二个就是欢迎页的支持,我们发现它将页面给转发到了index.html

在这里插入图片描述
回到正题,我们来看一下RequestMapping,我们可以看到,它往容器中放入了GET,POST,DELETE,PUT,还帮我们写好了 error 错误处理

在这里插入图片描述
并且,对于每个具体的请求,在 value 中都保存了相应的 Controller 位置,这也就是执行请求为什么会执行处理器中方法的原因
在这里插入图片描述
在往下,我们随便进入一个请求,比如进入 GET ,我们可以看到他在获取到了请求的路径之后还获取了一把锁,这个是为了防止多线程情况下出现数据安全问题
在这里插入图片描述
再往下看源码,我们发现对于获取到多个相同请求的情况下,系统会抛出个异常(这里的多个请求并不是指多线程情况下,而是你写代码的时候出现了同名请求的情况)

在这里插入图片描述

总结

所有的请求映射都在HandlerMapping中

  • SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
  • SpringBoot自动配置了默认的 RequestMappingHandlerMapping
  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个 HandlerMapping
  • 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping,自定义 HandlerMapping
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值