Spring MVC学习 | 视图&RESTFul

24 篇文章 0 订阅


学习视频🎥:https://www.bilibili.com/video/BV1Ry4y1574R

一、视图

1.1 视图对象View

💬概述:Spring MVC中的视图对象为View接口类型,跟Model接口一样,View接口也是Spring MVC的底层接口view

🔑作用:封装视图数据,对页面进行渲染,将model的模型数据展现给用户

🔑视图类型:Spring MVC默认的转发视图InternalResourceView和重定向视图RedirectView,如果引入了相关依赖,还有JstlView、ThymeleafView等

🔑在源码中查看视图对象

  • 在控制器方法中打上断点,debug模式启动,在方法栈中找到前端控制器DispatcherServlet的方法doDispatch()

  • 进入doDispatch()方法中,可以看到前端控制器获取mv对象之后,把mv对象传给processDispatchResult()方法,,该方法就是执行转发结果的方法,而mv对象中封装了视图信息(或者说视图对象),所以需要进入processDispatchResult()方法中查看视图对象processDispatchResult

  • processDispatchResult()方法中,mv对象又被传给render()方法,该方法实现了对视图进行渲染的功能 在这里插入图片描述

  • 进入render()方法中,可以看到一个resovleViewName()方法,该方法根据视图名对视图进行解析并返回一个解析之后的view对象,该对象就是Spring MVC中的视图对象resolveViewName

    💡 不同视图名称写法决定了解析之后获取到的视图对象的具体类型,即每一种视图对象对应的视图名称写法不同,下面详细介绍

1.2 ThymeleafView

💬概述:被thymeleaf视图解析器解析之后的视图就是ThymeleafView视图,即ThymeleafView类型的视图

💡 查看源码可知,ThymeleafView类间接实现了View接口

🔑对应的视图名称设置没有任何前缀和后缀,只有一个视图名,如"index"

🔑视图创建过程:执行控制器方法时,Spring MVC检测到返回值(视图名称)没有任何前缀和后缀,就会直接将视图名称交给在Spring MVC配置文件(springmvc.xml)中配置的thymeleaf视图解析器解析,然后thymeleaf视图解析器对该格式的视图名进行解析,此时得到的视图对象就为ThymeleafView类型

🔑测试

① 创建测试控制器方法,方法返回值,即设置的视图名称不添加任何前缀和后缀(与之前的写法一样)

@RequestMapping("testView01")
public String showTestView01() {
    return "success";
}

② debug模式启动,进入前端控制器DispatcherServletrender()方法中,查看view对象的具体类型信息view-thymeleafView

1.3 转发视图

💬概述:Spring MVC默认的转发视图类型为InternalResourceView,如果在工程中引入jstl相关依赖,则转发视图会自动转换成JstlView类型

🔑对应的视图名称设置:前缀——forward:再加上要转发的路径,如"forward:/testView01"

❓ 关于要转发的路径

  • 要转发的路径可以为/,整个视图名称为forward:/,则表示请求转发到首页
  • 要转发的路径可以写相对路径,如forward:testView01,但不推荐写相对路径(相对路径与绝对路径区别)

🔑视图的创建过程

💡 创建转发视图过程中,会生成多个不同类型的视图对象,因为转发过程会出现不同的视图名称,而且配置不同视图解析器也会有影响

① 执行控制器方法时,Spring MVC检测到返回值(视图名称)带了forward:的前缀,就不会将视图名称交给视图解析器解析,而是先将前缀forward:去掉,剩下部分作为请求转发的跳转路径(转发的URL),此时得到的视图对象为InternalResourceView类型(Spring MVC中的默认转发视图类型),然后Spring MVC会根据转发的路径找到与之匹配的控制器方法并执行
② 一般最后执行的控制器方法中,返回值都是没有前缀和后缀的视图名(目标页面),所以Spring MVC还会最后的视图名交给thymeleaf视图解析器解析,此时又会生成一个视图对象,为ThymeleafView类型,thymeleaf解析之后就直接跳转到目标页面

🔑测试

① 创建测试控制器方法,视图名称设置为forward:/testView01,表示请求转发到请求路径为/testView01的控制器方法showTestView01()

@RequestMapping("/testView02")
public String showTestView02() {
    return "forward:/testView01";
}

② 执行控制器方法showTestView02()时的view视图对象view-internalResourceView

③ 执行控制器方法showTestView01()时的视图对象view2-thymeleafView.

1.4 重定向视图

💬概述:Spring MVC中默认的重定向视图类型为RedirectView

🔑对应的视图名称设置:前缀——redirect:加上重定向跳转的路径,如"redirect:/testView02"

❓ 关于重定向的路径

  • 重定向的路径也可以为/,即整个视图名称设置为redirect:/,表示重定向到首页,一般在作用于增删改操作之后

  • 重定向视图在解析时,Spring MVC会检测重定向的路径(视图名称中去掉前缀的剩余部分),如果路径中有/,即路径为绝对路径时,Spring MVC会自动拼接上下文路径(工程路径),所以在写视图名称时只需写最终的请求路径或资源路径,不需要手动加上上下文路径

🔑视图的创建过程(与转发视图类似)

① 执行控制器方法时,Spring MVC检测到返回值(视图名称)带有redirect:的前缀,就不会把视图名称交给视图解析器解析,而是先将前缀redirect:去掉,剩余部分作为最终重定向跳转的路径(跳转的URL),此时得到的视图对象为RedirectView类型(Spring MVC中默认的重定向视图类型),然后Spring MVC再根据重定向的路径找到对应的控制器方法并执行(获取到重定向路径时已经是浏览器发起的第二次请求)
② 一般最后执行的控制器方法中,返回值都是没有前缀和后缀的视图名(目标页面),所以Spring MVC还会最后的视图名交给thymeleaf视图解析器解析,此时又会生成一个视图对象,为ThymeleafView类型,thymeleaf解析之后就直接跳转到目标页面

🔑测试

① 创建测试控制器方法,视图名称设置为redirect:/testView01,表示重定向到请求路径为/testView01的控制器方法showTestView01()

@RequestMapping("/testView03")
public String showTestView03() {
    return "redirect:/testView01";
}

② 执行控制器方法showTestView03()时的视图对象view
view-redirectView

③ 执行控制器方法showTestView01()时的视图对象
view2-thymeleafView

1.5 视图控制器

💬概述:如果某个控制器只是用来实现页面的跳转,没有进行其他处理,即仅仅设置了一个视图名并返回,则可以在Spring MVC配置文件中采用视图控制器标签设置请求路径和视图名,代替该控制器方法

🔑使用

① 先在Spring MVC配置文件,即springmvc.xml中添加<mvc:view-controller>标签,在标签设置请求路径和视图名

<mvc:view-controller path="/testView01" view-name="success"></mvc:view-controller>

🔺 <mvc:view-controller>标签的两个属性

  1. path:设置请求路径,对应控制器方法中@RequestMapping注解的value属性值
  2. view-name:设置视图名称,对应控制器方法的返回值

② 再添加一个<mvc:annotation-driven/>标签,用于开启注解驱动,必须要开启

<mvc:annotation-driven/>

❓ 为什么要开启注解驱动:在springmvc.xml中添加了任何一个控制器方法对应的视图控制器标签后,所有其他的控制器(或者说控制器方法)的请求映射将自动失效,即Spring MVC匹配不到所有的控制器方法,此时必须在springmvc.xml(核心配置文件)中添加开启注解驱动的标签,该标签以后经常用到,所以一般springmvc.xml中都需要开启注解驱动


二、RESTFul

2.1 简介

💬概述

  • RESTFul中的REST --> Representational State Transfer,意思是表现层资源状态转移
  • RESTFul是一种资源操作、资源定位的风格,是基于REST搭建的API,不是一个标准、也不是一种协议,仅仅是一种风格

🔑Http动词设计
1. GET --> get请求,对应查询操作(Read)
2. POST --> post请求,对应添加操作(Create)
3. PUT --> put请求,对应修改操作(Update)
4. DELETE --> delete请求,对应删除操作(Delete)

🔑REST风格的路径设计:RESTFul风格提倡路径(URL地址)采用统一的风格设计,即从前到后每个单词用斜杠/分开,不使用?加键值对的方式添加请求参数,而是将请求参数作为路径的一部分,同样使用/分开,以保证整体风格的统一。四个基本操作CRUD、Http动词、传统路径设计以及RESTFul风格的路径设计关系表如下

基本操作(CRUD)Http动词传统路径设计RESTFul路径设计描述
查询操作GET/getUserById?id=1/user/1查询id为1的用户信息
添加操作POST/addUserInfo/user添加一条用户信息
修改操作PUT/updateUserInfo/user修改用户信息
删除操作DELETE/deleteUserById?id=1/user/1删除id为1的用户信息

💡 Spring MVC学习 | @RequestMapping注解#value对REST风格的路径书写有过简单介绍
💡 RESTFul风格的路径设计中,添加操作和修改操作、查询操作和删除操作的路径分别是一样的,但因为它们的请求方式,即对应的Http动词不同,所以实际处理也会不同,这也是为什么RESTFul风格能保持统一的原因

2.2 PUT和DELETE请求的实现

2.2.1 HiddenHttpMethodFilter过滤器

🔍引入问题:在RESTFul风格下实现修改和删除操作需要使用PUT和DELETE请求方式,但当前浏览器只支持GET和POST请求,不支持PUT和DELETE请求,那么该如何解决?

🔑问题解决:使用Spring MVC提供的内部过滤器——HiddenHttpMethodFilter

🔑HiddenHttpMethodFilter介绍

  • 功能:可以将POST请求转变成PUT或DELETE请求(还能转变成PATH请求)

  • 使用条件

    ① 在web.xml文件中配置

    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    💡 在web.xml中配置HiddenHttpMethodFilter过滤器时,必须配置在CharacterEncodingFilter编码过滤器后面,不然设置编码的操作会失效(具体原因查看前面)

    ② 当前请求必须设置为POST请求

    ③ 当前请求必须带上一个请求参数,请求参数名必须为_method,而请求参数值设置为真正需要的请求方法(PUT、DELETE)

💡 当满足上述后面两个条件时,HiddenHttpMethodFilter过滤器会将当前请求方式POST转变为请求参数_method的参数值,因此_method的值才是最终的请求方式,也就是我们想要设置的请求方式(PUT、DELETE),具体怎么转变参见源码分析

🔑分析HiddenHttpMethodFilter源码

① 对于过滤器,最重要的方法就是执行过滤的方法,在HiddenHttpMethodFilter源码中可以找到doFilterInternal()方法,该方法就是执行过滤的方法(与CharacterEncodingFilter的执行过滤方法类似)hiddenHttpMethodFilter-doFilter

② 进入doFilterInternal()方法中,可以看到过滤器先通过request.getMethod()方法获取当前请求的请求方式,判断当前请求方法是否为POST,如果是POST才继续操作,所以使用HiddenHttpMethodFilter过滤器的条件之一是当前请求必须设置为POSThiddenHttpMethodFilter-doFilter-detail

③ 在当前请求方式是POST的情况下,再获取当前请求中的请求参数名为this.methodParam的请求参数值,点击this.methodParam可以看到,该请求参数其实就是_method,即我们在表单中添加的隐藏域表单项(或者说请求参数),表单项名就是_method,所以这也是使用HiddenHttpMethodFilter的条件之一。再获取到_method的参数值之后,在有长度(不为空)的情况下将其转成大写字母并赋值给method,此时的method值就是我们想要设置的请求方法(PUT、DELETE)hiddenHttpMethodFilter-_method

④ 最后我们看到一个ALLOWED_METHODS变量,点击该变量,可以看到它就是一个List集合,集合中有三个固定常量参数,分别为PUT、DELETE和PATCH,当该集合包含method时,就创建HiddenHttpMethodFilter中一个内部类对象requestToUse,该内部类为HttpMethodRequestWrapper,该内部类的所用就是将当前请求对象中的请求方式换成method值对应的请求方式(即PUT、DELETE、PATCH中的一个),创建出来的对象就是更新请求方式后的请求对象hiddenHttpMethodFilter-ALLOWED_METHODS

⑤ 进入内部类HttpMethodRequestWrapper中,可以看到类中将当前请求对象的getMethod()方法进行重写,返回新的请求方式,该新的请求方式就是doFilterInternal()方法中传入的method值,从而实现请求对象中请求方式的更新hiddenHttpMethodFilter-HttpMethodRequestWrapper

2.2.2 实现PUT请求

① 在web.xml文件中配置hiddenHttpMethodFilter过滤器

② 创建测试控制器方法,方法的@RequestMapping注解添加method属性,属性值设置为RequestMethod.PUT,并通过方法形参获取请求参数

@RequestMapping(
    value = "/user",
    method = RequestMethod.PUT
)
public String updateUserInfo(String username, String password) {
    System.out.println("PUT --> 修改用户信息");
    System.out.println("用户名 --> " + username);
    System.out.println("密码 --> " + password);
    return "success";
}

③ 创建测试表单,<form>标签中的method属性值设置为post,即表单的请求方式设置为post,然后在表单内添加一个隐藏域,隐藏域为表单项之一,name属性设置为_methodvalue设置为put(大写也可以)

<form th:action="@{/user}" method="post">
    <!-- 添加隐藏域 -->
    <input type="hidden" name="_method" value="put">
    用户名:<input type="text" name="username"><br/>
    密码:<input type="password" name="password"><br/>
    <input type="submit" value="修改信息">
</form>

❓ 为什么添加隐藏域:因为使用HiddenHttpMethodFilter过滤器的前提是需要添加请求参数_method,表单中每一个表单项就对应一个请求参数,所以需要添加一个表单项来传输_method参数,而该请求参数即该表单项与用户无关,无需展示到页面,因此需要添加隐藏域

2.2.3 实现DELETE请求

🔺 删除功能的实现思路:删除功能的实现同样需要将请求方式设置为POST请求,然后添加隐藏域带上请求参数_method,参数值设置为delete,但POST请求和隐藏域只能在表单元素中添加,而删除功能一般只用一个简单的超链接实现,然而超链接的请求方式是GET,所以实现删除功能需要创建删除的超链接和对应的提交表单,然后使用JS(这里采用vue来实现)给超链接绑定单击事件,当点击超链接时把链接赋值给表单的action属性,最后再提交

① 在web.xml文件中配置HiidenHttpMethodFilter过滤器

② 创建控制器方法,方法的@RequestMapping注解添加method,属性值设置为RequestMethod.DELETEvalue属性值中采用占位符代替请求参数id,并添加请求参数id对应的形参,形参上添加@PathVariable注解,方法返回值需要设置为重定向视图对应的视图名称(一般增删改操作之后需要使用重定向回到首页)

@RequestMapping(
    value = "/user/{id}",
    method = RequestMethod.DELETE
)
public String deleteUserById(@PathVariable("id") String id) {
    System.out.println("删除用户信息 --> DELETE");
    System.out.println("被删除用户的id --> " + id);
    return "redirect:/success";
}

③ 在test-user.html中先创建一个删除超链接,超链接的href属性中需要带上一个请求参数id值,然后在<a>标签中添加id@click属性,用于绑定单击事件

<a id="delAId" @click="deleteUser" th:href="@{/user/2}">删除用户信息</a>

❓ 如果请求参数需要动态获取:这里演示的id请求参数是写死的,项目中是需要动态获取,如果需要从域对象中获取,则需要将前面的路径与获取参数的表达式(${内置对象.域对象中的数据名})拼接起来,有两种拼接方式,比如从request域中获取user对象,再获取user对象的id值

  1. 将表达式直接拼接在@{}中,此时需要将前面的路径写到单引号''里面:th:href="@{'/user/' + ${user.id}"
  2. 将表达式拼接在@{}后面:th:href="@{/user} + ${user.id}"

④ 再添加一个表单,表单元素中无需添加action属性,只需设置method属性,并设置为post,然后在表单中添加一个隐藏域,隐藏域中设置表单项名_method(请求参数名),值设置为delete,无需添加其他表单项,也无需添加提交按键

<form id="delFormId" method="post">
    <input type="hidden" name="_method" value="delete">
</form>

⑤ 引入vue.js文件,放到webapp/static/js目录下,然后在test-user.html中通过<script>标签引入文件,然后再使用vue操作超链接和表单两个元素,处理点击事件

<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript">
    // 使用vue处理点击事件
    let vue = new Vue({
        el:"#delAId",
        methods:{
            // event表示当前事件
            deleteUser:function (event) {
                // 通过id获取表单元素
                let delForm = document.getElementById("delFormId");

                // 将超链接的href属性值赋值给表单的action属性
                delForm.action = event.target.href;

                // 提交表单
                delForm.submit();

                // 阻止超链接的默认跳转行为
                event.preventDefault();
            }
        }
    });
</script>

⑥ 在工程中添加的静态资源,一般不会自动加载(即不会出现在maven工程的target目录下),所以需要先手动重新打包,即执行maven的package命令,然后在springmvc.xml文件中开放对静态资源的访问(使用默认的servlet来处理静态资源)

<!-- 开放对静态资源的访问 -->
<mvc:default-servlet-handler/>

❓ 关于静态资源的访问

  • 在Spring MVC中,浏览器发送的请求包括一些静态资源首先统一交给前端控制器DispatcherServlet处理,而前端控制器会直接将静态资源当作普通的请求路径来处理,由于找不到静态资源所对应的映射关系(或者说找不到静态资源对应的控制器方法),Spring MVC会认为找不到对应资源,因此没有在springmvc.xml中配置<mvc:default-servlet-handler/>标签的情况下,点击删除链接后页面就会报404错误
  • 既然前端控制器无法识别静态资源并处理,就需要将静态资源交给tomcat服务器中默认的servletDefaultServlet处理,而使用默认servlet就必须在springmvc.xml中开放对静态资源的访问
  • 在springmvc.xml中必须同时配置开放静态资源的访问和开启注解驱动,即<mvc:default-servlet-handler/><mvc:annotation-driven/>两个标签必须同时加上,如果只加前者,则所有请求都交给默认servlet处理,此时就只有静态资源能被处理,其它请求资源都无法被默认servlet处理;如果只加后者,就无法处理静态资源,因此必须两个都加上,缺一不可

❓ 关于服务默认的servlet

  • 服务器默认的servlet——DefaultServlet配置在tomcat的全局配置文件web.xml中,配置的资源路径跟前端控制器一样,都是为/
  • 因为tomcat中的web.xml文件是部署到全部tomcat工程,而当前工程中的web.xml仅部署到当前工程,如果两个web.xml文件中的配置发生冲突时,如DefaultServletDispatcherServlet的资源路径都是/,会以当前工程中的web.xml为准(就近原则),所以浏览器发起的请求会先经过DispatcherServlet,而不是先经过DefaultServlet
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值