2022尚硅谷SSM框架跟学 七 Spring MVC基础二
- 4.SpringMVC获取请求参数
- 4.1.通过ServletAPI获取
- 4.2.通过控制器方法的形参获取请求参数
- 4.3.@RequestParam
- 4.4.@RequestHeader
- 4.5.@CookieValue
- 4.6.通过POJO获取请求参数
- 4.7.解决获取请求参数的乱码问题
- 5.域对象共享数据
- 5.1.使用ServletAPI向request域对象共享数据
- 5.2.使用ModelAndView向request域对象共享数据
- 5.3.使用Model向request域对象共享数据
- 5.4.使用map向request域对象共享数据
- 5.5.使用ModelMap向request域对象共享数据
- 5.6.Model、ModelMap、Map的关系
- 5.7.向session域共享数据
- 5.8.向application域共享数据
- 6.SpringMVC的视图
- 6.1.ThymeleafView
- 6.2.转发视图(不常用)
- 6.3.重定向视图
- 6.4.视图控制器view-controller
- 7.RESTful
- 7.1.RESTful简介
- (1).资源
- (2).资源的表述
- (3).状态转移
- 7.2.RESTful的实现
- (1).查询所有用户信息
- (2).查询id = 1的用户信息
- (3).添加用户信息
- 7.3.HiddenHttpMethodFilter
- (1).修改用户信息
- (2).删除 id = 5的用户信息
- (3).派生注解
4.SpringMVC获取请求参数
创建新的测试控制层TestParamController
修改index.html增加代码
<hr>
<form th:action="@{/param/servletAPI}" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="登录"><br/>
</form>
4.1.通过ServletAPI获取
将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象
修改TestParamController.java
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* @ClassName: TestParamController
* @Description:
* @Author: wty
* @Date: 2023/1/29
*/
@Controller
public class TestParamController {
@RequestMapping("/param/servletAPI")
public String getParamByServletAPI(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:" + username);
System.out.println("password:" + password);
return "success";
}
}
重新部署Tomcat
输入账号和密码
点击登录后,成功跳转
看一下控制台的输出结果
总结
通过servletAPI获取:只需要在控制器方法的形参位置HttpServletRequest类型的形参,就可以在控制器方法中使用request对象获取请求参数。
4.2.通过控制器方法的形参获取请求参数
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参。
修改index.html
<hr>
<form th:action="@{/param}" method="post">
用户名:<input type="text" name="name"><br/>
密码:<input type="password" name="pwd"><br/>
<input type="submit" value="登录"><br/>
</form>
修改TestParamController.java,新增方法
@RequestMapping("/param")
public String getParamByParam(String name, String pwd) {
System.out.println("name:" + name);
System.out.println("pwd:" + pwd);
return "success";
}
重新部署Tomcat
输入用户名和密码后,成功跳转
看一下控制台输出的结果
总结
通过控制器方法的形参获取
只需要在控制器方法的形参位置,设置一个形参,形参的名字和请求参数的名字一致即可。
注:
若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串、数组或者字符串类型的形参接收此请求参数。
若使用字符串数组类型的形参,此参数的数组中包含了每一个数据。
若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果。
验证以上结论,我们看下,如果获取的参数名和实际参数名不一致会怎么样呢?
修改index.html,把name的n写成大写N
再重新部署一下Tomcat,输入账号和密码
跳转成功
看一下控制台的结果,用户名得到的是null
那我们就需要手动建立实际参数和指定参数之间的映射关系,需要引入注解@RequestParam
4.3.@RequestParam
修改TestParamController.java
@RequestMapping("/param")
public String getParamByParam(@RequestParam(value = "Name", required = true) String name, String pwd) {
System.out.println("name:" + name);
System.out.println("pwd:" + pwd);
return "success";
}
看一下RequestParam.class的源码,
可以发现required属性默认是true(必填)
重新部署Tomcat输入账号和密码
跳转成功
查看控制台的结果
那如果我们手动输入地址,没有放入Name这个required = true 的参数会发生什么呢?
跳转网址
http://localhost:8080/SpringMVC/param?Name=jackpassword=123
网站报错
HTTP Status 400 - Required String parameter ‘Name’ is not present
那如果我们加上Name呢
跳转网站
http://localhost:8080/SpringMVC/param?Name=jackpassword=123
跳转成功
修改TestParamController.java
required = false设置为不必填
defaultValue默认值设置为hello
@RequestMapping("/param")
public String getParamByParam(@RequestParam(value = "Name", required = false, defaultValue = "hello") String name, String pwd) {
System.out.println("name:" + name);
System.out.println("pwd:" + pwd);
return "success";
}
再次跳转网址看一下
http://localhost:8080/SpringMVC/param?password=123
成功跳转
控制台输出结果
可以看出来,如果跳转的网址没有加入参数Name,就会展示默认值。
总结
@RequestParam是将请求参数和控制器方法的形参创建映射关系
@RequestParam注解一共有三个属性:
value:指定为形参赋值的请求参数的参数名。
required:设置是否必须传输此请求参数,默认值为true。
若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错
400:Required String parameter ‘xxx’ is not present;
若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null。
defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值。
4.4.@RequestHeader
将请求头信息和控制器方法的形参绑定。
按F12,比如,现在想获取请求头中Referer这个参数的信息
这里就要引入注解@RequestHeader
先看一下RequestHeader.class的源码
修改TestParamController.java
@RequestMapping("/param")
public String getParamByParam(
@RequestParam(value = "Name", required = false, defaultValue = "hello") String name,
String pwd,
@RequestHeader("Referer") String referer
) {
System.out.println("name:" + name);
System.out.println("pwd:" + pwd);
System.out.println("referer:" + referer);
return "success";
}
重新部署Tomcat看一下
输入用户名和密码,跳转后查看参数Referer
查看控制台,输出没有问题
这里注意请求头首字母,大小写不区分,所以写成以下的小写形式也是可以的。
@RequestHeader("referer")
总结
@RequestHeader是将请求头信息和控制器方法的形参创建映射关系
@RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam。
4.5.@CookieValue
将cookie数据和控制器方法的形参绑定。
先看一下源码CookieValue.class
修改TestParamController.java
在getParamByServletAPI方法中增加获取session
在getParamByParam方法中增加jsessionId
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* @ClassName: TestParamController
* @Description:
* @Author: wty
* @Date: 2023/1/29
*/
@Controller
public class TestParamController {
@RequestMapping("/param/servletAPI")
public String getParamByServletAPI(HttpServletRequest request) {
HttpSession session = request.getSession();
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:" + username);
System.out.println("password:" + password);
return "success";
}
@RequestMapping("/param")
public String getParamByParam(
@RequestParam(value = "Name", required = false, defaultValue = "hello") String name,
String pwd,
@RequestHeader("referer") String referer,
@CookieValue("JSESSIONID") String jsessionId
) {
System.out.println("name:" + name);
System.out.println("pwd:" + pwd);
System.out.println("referer:" + referer);
System.out.println("jsessionId:" + jsessionId);
return "success";
}
}
重新部署Tomcat
产生JSESSIONID
之后再跑下面的功能
发现携带了cookie
看一下控制台
总结
@CookieValue是将cookie数据和控制器方法的形参创建映射关系
@CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam。
4.6.通过POJO获取请求参数
创建新实体类User.java
代码如下:
package com.atguigu.pojo;
/**
* @ClassName: User
* @Description:
* @Author: wty
* @Date: 2023/1/29
*/
public class User {
private Integer id;
private String password;
private String username;
public User() {
}
public User(Integer id, String password, String username) {
this.id = id;
this.password = password;
this.username = username;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", password='" + password + '\'' +
", username='" + username + '\'' +
'}';
}
}
修改index.html
<hr>
<form th:action="@{/param/pojo}" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="登录"><br/>
</form>
修改TestParamController.java增加新方法
@RequestMapping("/param/pojo")
public String getParamByPojo(User user) {
System.out.println("user:" + user);
return "success";
}
重新部署Tomcat,输入账号和密码
跳转成功
控制台输出结果,把user的信息输出了
总结
可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值。
4.7.解决获取请求参数的乱码问题
先看一下乱码问题是怎么样的,登录的用户名输入中文
跳转结果
查看控制台,发现用户名是?
那我们把post请求更改成get会怎样呢
修改index.html
<form th:action="@{/param/pojo}" method="get">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="登录"><br/>
</form>
再重新部署一下Tomcat,看一下控制台的结果,发现竟然正常显示了
原因是Tomcat8之后的版本,都自动解决了get请求乱码的问题,而post请求乱码需要我们手动解决。
修改web.xml,增加过滤器
<!-- 配置spring的编码过滤器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
看一下CharacterEncodingFilter的源码
encoding的设置
forceEncoding的设置
源码会设置请求和响应的编码
修改index.html,变更为post
输入用户名和密码
跳转成功
再看看控制台没有乱码了
总结
解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是必须在web.xml中进行注册
注:
SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效。
5.域对象共享数据
创建TestScopeController.java
5.1.使用ServletAPI向request域对象共享数据
在index.html中增加
<br/>
<a th:href="@{/ServletAPI}">使用ServletAPI向request域对象共享数据</a>
在TestScopeController.java中增加
@Controller
public class TestScopeController {
@RequestMapping("/ServletAPI")
public String testServletAPI(HttpServletRequest request) {
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
}
在success.html中增加
<p th:text="${testScope}"></p>
点击超链接跳转
成功显示设置的共享数据
5.2.使用ModelAndView向request域对象共享数据
在index.html中增加
<a th:href="@{/test/mav}">使用ModelAndView向request域对象共享数据</a>
在TestScopeController.java中增加
/**
* ModelAndView有Model和View的功能
* Model主要用于向请求域共享数据
* View主要用于设置视图,实现页面跳转
*/
@RequestMapping("/test/mav")
public ModelAndView testMAV() {
ModelAndView mav = new ModelAndView();
//向请求域共享数据
mav.addObject("testRequestScope", "hello,ModelAndView");
//设置视图,实现页面跳转
mav.setViewName("success");
return mav;
}
在success.html中增加
<p th:text="${testRequestScope}"></p><br/>
重新部署Tomcat
点击超链接之后,发现成功展示了共享数据
总结
通过ModelAndView向请求域共享数据
使用ModelAndView时,可以使用其Model功能向请求域共享数据,使用View功能设置逻辑视图,但是控制器方法一定要将ModelAndView作为方法的返回值。
5.3.使用Model向request域对象共享数据
在index.html中添加
<a th:href="@{/test/model}">使用Model向request域对象共享数据</a><br/>
在TestScopeController.java中添加代码
@RequestMapping("/test/model")
public String testModel(Model model) {
model.addAttribute("testModel", "hi");
return "success";
}
修改success.html
<p th:text="${testModel}"></p><br/>
重新部署Tomcat
单击超链接跳转后,成功加载共享数据
5.4.使用map向request域对象共享数据
在index.html中增加
<a th:href="@{/test/map}">使用map向request域对象共享数据</a><br/>
在TestScopeController.java中增加
@RequestMapping("/test/map")
public String testMap(Map<String, Object> map) {
map.put("testMap", "hi,i am map");
return "success";
}
在success.html中增加
<p th:text="${testMap}"></p><br/>
重新部署Tomcat
单击超链接,发现成功展示共享数据
5.5.使用ModelMap向request域对象共享数据
在index.html中增加
<a th:href="@{/test/testModelMap}">使用ModelMap向request域对象共享数据</a><br/>
在TestScopeController.java中增加
@RequestMapping("/test/testModelMap")
public String testModelMap(ModelMap modelMap) {
modelMap.addAttribute("modelMap", "hello,i am modelMap");
return "success";
}
在success.html中增加
<p th:text="${modelMap}"></p><br/>
重新部署Tomcat
点击超链接之后,成功跳转展示共享数据的内容
5.6.Model、ModelMap、Map的关系
我们修改TestScopeController.java,让其打印三者运行时类型
在testModel方法中增加
System.out.println("model:" + model.getClass().getName());
发现打印控制台的结果
在testMap方法中增加
System.out.println("map:" + map.getClass().getName());
发现打印控制台的结果
在testModelMap方法中增加
System.out.println("modelMap:" + modelMap.getClass().getName());
发现打印控制台的结果
发现三者本质都是同一个类型
model:org.springframework.validation.support.BindingAwareModelMap
总结
Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的
public interface Model{}
public class LinkedHashMap<K, V> extends HashMap<K, V> implements Map<K, V>
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}
5.7.向session域共享数据
在index.html中添加
<a th:href="@{/test/testsession}">向session域共享数据</a><br/>
在TestScopeController.java中添加代码
@RequestMapping("/test/testsession")
public String testSession(HttpSession session) {
session.setAttribute("testSession", "hello, session");
return "success";
}
在success.html中添加代码
<p th:text="${session.testSession}"></p><br/>
重新部署Tomcat得到
跳转后得到session域共享数据
5.8.向application域共享数据
在index.html中添加
<a th:href="@{/test/testapplication}">向application域共享数据</a><br/>
在TestScopeController.java中添加代码
@RequestMapping("/test/testapplication")
public String testapplication(HttpSession session) {
ServletContext servletContext = session.getServletContext();
servletContext.setAttribute("testApplication", "hello, application");
return "success";
}
在success.html中添加代码
<p th:text="${application.testApplication}"></p><br/>
重新部署Tomcat得到
单击超链接得到application域共享数据
这里看到同样展示了session作用域的数据,那是因为session作用域的范围是浏览器的,那么我们复制地址,关掉浏览器用其它浏览器重新再打开一次。我们选择主页
http://localhost:8080/SpringMVC/
然后单击map的超链接
发现session域和application的共享都没有了
那么我们想保留session的数据呢,很简单,我们设置Tomcat的配置,在保留session共享数据这里打勾即可。
设置完毕后我们重新部署Tomcat,
然后我们选择主页
http://localhost:8080/SpringMVC/
然后单击session的超链接
跳转后显示session的共享数据
然后单击application的超链接
跳转后显示application的共享数据
然后单击map的超链接
跳转后session、map、application都显示了
现在再重新部署Tomcat
刷新当前网址
http://localhost:8080/SpringMVC/test/map
发现session仍然存在,application没有了
6.SpringMVC的视图
SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户。
SpringMVC视图的种类很多,默认有转发视图和重定向视图
当工程引入jstl的依赖,转发视图会自动转换为JstlView。
若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView。
比如,我们现在配置的springmvc.xml文件中配置的是ThymeleafViewResolver
在TestScopeController.java类中的testMAV方法的第一行打上断点,之后点击超链接跳转
进入断点后,找到Debugger中的DispatcherServlet打上断点,发现mv也是ModelAndView类型的。
同理在model共享数据域的方法testModel中打上断点,也是跳转到相同的代码。
跳转到DispatcherServlet的断点。
同样的,上一章讲述的所有共享数据域:
1.使用ServletAPI向request域对象共享数据
2.使用ModelAndView向request域对象共享数据
3.使用Model向request域对象共享数据
4.使用map向request域对象共享数据
5.使用ModelMap向request域对象共享数据
最终都是通过中央控制器DispatcherServlet类中的ModelAndView方式来共享数据的。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
以map保存域为例,打个断点看一下mv的存储结果
6.1.ThymeleafView
修改index.html,增加代码
<hr>
<a th:href="@{/test/view/thymeleaf}">测试SpringMVC的视图ThymeleafView</a>
新建类TestViewController.java
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @ClassName: TestViewController
* @Description:
* @Author: wty
* @Date: 2023/1/30
*/
@Controller
public class TestViewController {
@RequestMapping("/test/view/thymeleaf")
public String testThymeleafView() {
return "success";
}
}
给testThymeleafView方法第一行打上断点
重新部署Tomcat然后点击超链接
进入断点
之后点击F7进入方法processDispatchResult,主要看视图渲染
按F7进入render方法,看到视图渲染的类型,是根据逻辑视图的名称来判断的,当前没有前缀,就是ThymeleafView
总结:
当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转。
6.2.转发视图(不常用)
修改index.html,增加代码
注意,这种方式不常用,因为重新转发后的视图,不会经过Thymeleaf渲染,所以该方式不推荐。
<a th:href="@{/test/view/forward}">测试SpringMVC的视图InternalResourceView</a><br/>
修改TestViewController.java增加方法
@RequestMapping("/test/view/forward")
public String testInternalResourceView() {
return "forward:/test/model";
}
重新部署Tomcat
点击跳转的超链接,进入断点
进入断点后,因为是服务器端的转发,那么会再进入一次断点,服务端转发后这次就是ThymeleafView视图,因为这次的视图名是success没有前缀。
最后的网址为
总结:
SpringMVC中默认的转发视图是InternalResourceView
SpringMVC中创建转发视图的情况:
当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:“去掉,剩余部
分作为最终路径通过转发的方式实现跳转,例如"forward:/”,"forward:/employee
6.3.重定向视图
修改index.html
<a th:href="@{/test/view/redirect}">测试SpringMVC的视图RedirectView</a><br/>
修改TestViewController.java
@RequestMapping("/test/view/redirect")
public String testRedirectView() {
return "redirect:/test/model";
}
重新部署Tomcat
点击超链接跳转,之后进入断点,进入断点后获取视图RedirectView
客户端重定向,会再进入一次断点,此时就是ThymeleafView视图,因为这次的视图名是success没有前缀。
最终的网站变为重定向后的地址
总结
SpringMVC中默认的重定向视图是RedirectView
当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:“去掉,剩余部分作为最
终路径通过重定向的方式实现跳,转例如"redirect:/”,“redirect:/employee”
注:
重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头,若是则会自动拼接上下文路径。
6.4.视图控制器view-controller
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示。
修改springmvc.xml
<!--
视图控制器view-controller
仅仅用来实现页面跳转
path:设置处理的请求地址
view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
注释掉PortalController.java中的跳转index界面的方法
重新部署Tomcat,发现仍然可以正常的跳转到首页
但是里面的超链接点击后,都会报错404
HTTP Status 404 -
这里的原因是,如果当SpringMVC中设置任何一个view-controller时,只有该设置会通过中央控制器来加载,而其他控制器中的请求映射将全部失效。解决办法就是加上注解驱动
修改springmvc.xml,加上代码
<!-- 开启mvc的注解驱动 -->
<mvc:annotation-driven/>
再次重新部署Tomcat,点击超链接,成功跳转
注:
当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:
< mvc:annotation-driven />
7.RESTful
7.1.RESTful简介
REST:Representational State Transfer,表现层资源状态转移。
(1).资源
资源是一种看待服务器的方式,即:将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。
(2).资源的表述
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格
式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
(3).状态转移
状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。
7.2.RESTful的实现
具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。
REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
操作 | 传统方式 | REST风格 |
---|---|---|
查询操作 | getUserById?id=1 | user/1–>get请求方式 |
保存操作 | saveUser | user–>post请求方式 |
删除操作 | deleteUser?id=1 | user/1–>delete请求方式 |
更新操作 | updateUser | user–>put请求方式 |
简单看一下REST风格,以百度百科为例,搜索RESTful
更换一下词条,发现前面一致,只是后面搜索的词条变了
创建一个新的工程
Name:spring-mvc-rest
GroupId:com.atguigu
在pom.xml中添加代码,增加打包方式
<packaging>war</packaging>
把之前helloworld中的pom.xml拷贝到到当前
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
之后配置web.xml,配置路径如下:
F:\javawebwork\SSM\spring-mvc-rest\src\main\webapp\WEB-INF\web.xml
如图所示
之后在web.xml中增加代码
<!-- 设置Spring的编码过滤器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 设置springMVC的前端控制器 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 将前端控制器的启动时间提前到服务启动时 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
创建控制层TestRestController.java
创建配置文件springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 扫描控制层组件(自己配置package) -->
<context:component-scan base-package="com.atguigu.controller"></context:component-scan>
<!-- 配置Thymeleaf视图解析器(直接拷贝即可) -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--
比如当前index的实际位置在/WEB-INF/templates/index.html(物理视图)
去掉视图前缀变为 index.html
去掉视图后缀变为 index(逻辑视图)
-->
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!-- 配置视图控制器 -->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<!-- 开启mvc注解驱动 -->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
</beans>
创建目录templates
(1).查询所有用户信息
在目录templates创建index.html作为首页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>index.html</h1>
<a th:href="@{/user}">查询所有的用户信息</a>
</body>
</html>
新建success.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>跳转成功</title>
</head>
<body>
<h1>success.html</h1>
</body>
</html>
在TestRestController.java中添加方法
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@Controller
public class TestRestController {
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getAllUser() {
System.out.println("查询操作 getAllUser user-->get请求方式");
return "success";
}
}
重新配置和部署Tomcat
设置Artifacts
配置启动参数
启动Tomcat,点击超链接
发现成功跳转
(2).查询id = 1的用户信息
修改index.html增加代码
<a th:href="@{/user/1}">查询id为1的用户信息</a><br>
修改TestRestController.java
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public String getUserById(@PathVariable("id") Integer id) {
System.out.println("根据id查询用户信息操作 getUserById user/" + id + "-->get请求方式");
return "success";
}
部署Tomcat之后,点击超链接
发现成功跳转,并且地址加载出了1
再看下控制台,也获取到了1
(3).添加用户信息
修改index.html,因为是保存信息,是需要提交表单的,所以用了post请求。
<form th:action="@{/user}" method="post">
<input type="submit" value="添加用户信息">
</form><br>
修改TestRestController.java
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String insertUser() {
System.out.println("添加用户信息 insertUser user-->post请求方式");
return "success";
}
重新部署Tomcat,点击form表单的按钮
发现跳转成功
再看一下控制台的信息
7.3.HiddenHttpMethodFilter
(1).修改用户信息
修改index.html
<form th:action="@{/user}" method="put">
<input type="submit" value="修改用户信息">
</form>
<br>
修改TestRestController.java
@RequestMapping(value = "/user", method = RequestMethod.PUT)
public String updateUser() {
System.out.println("修改用户信息 updateUser user-->put请求方式");
return "success";
}
重新部署Tomcat
点击form表单的按钮,发现成功跳转但是地址有问号
再看一下控制台的信息,竟然匹配成了查询
这就说明了,当前浏览器的请求方式发送过来就是get,并不是我们写了put就变成了put,method只能写post和get
这个时候需要修改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>
修改index.html
(1)修改method请求方式为post
(2)增加 name=“_method” 的input标签
<form th:action="@{/user}" method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="修改用户信息">
</form>
<br>
这次我们再重新部署一下Tomcat,可以看到成功跳转
我们再看一下控制台,成功输出信息
(2).删除 id = 5的用户信息
修改index.html
<form th:action="@{/user/5}" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="删除用户信息">
</form>
<br>
修改TestRestController.java
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public String deleteUser(@PathVariable("id") Integer id) {
System.out.println("删除用户信息 deleteUser user/" + id + "-->delete请求方式");
return "success";
}
重新部署Tomcat,点击删除的form表单
发现成功跳转
再看一下控制台的信息
总结
由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?
SpringMVC 提供了HiddenHttpMethodFilter帮助我们将 POST请求转换为DELETE或PUT请求。
HiddenHttpMethodFilter处理put和delete请求的条件:
< a >.当前请求的请求方式必须为post
< b >.当前请求必须传输请求参数_method
满足以上条件,HiddenHttpMethodFilter过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式。
在web.xml中注册HiddenHttpMethodFilter。
<!-- 设置处理请求方法的过滤器 -->
<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>
下面呢我们来看一下,这个过滤器的源码HiddenHttpMethodFilter.class
首先我们看到其继承类我们比较眼熟
public class HiddenHttpMethodFilter extends OncePerRequestFilter
想了想,编码过滤器CharacterEncodingFilter.class也继承自相同的类
public class CharacterEncodingFilter extends OncePerRequestFilter
打开alt+7我们查看源码的方法,过滤器选择这个即可。
查看源码
有参构造方法
注:
目前为止,SpringMVC中提供了两个过滤器:
CharacterEncodingFilter和HiddenHttpMethodFilter。
在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter。
原因:
在 CharacterEncodingFilter 中通过request.setCharacterEncoding(encoding) 方法设置字符集的request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作。
而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作:
String paramValue = request.getParameter(this.methodParam);
(3).派生注解
最后我们用派生注解的方式来解决一下TestRestController.java
修改注解
//@RequestMapping(value = "/user", method = RequestMethod.GET)
@GetMapping("/user")
//@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
@GetMapping("/user/{id}")
//@RequestMapping(value = "/user", method = RequestMethod.POST)
@PostMapping("/user")
//@RequestMapping(value = "/user", method = RequestMethod.PUT)
@PutMapping("/user")
//@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
@DeleteMapping("/user/{id}")
这里贴一下全部代码方便查看
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
/**
* @ClassName: TestRestController
* @Description: 查询操作 getUserById?id=1 user/1-->get请求方式
* 保存操作 saveUser user-->post请求方式
* 删除操作 deleteUser?id=1 user/1-->delete请求方式
* 更新操作 updateUser user-->put请求方式
* @Author: wty
* @Date: 2023/1/30
*/
@Controller
public class TestRestController {
//@RequestMapping(value = "/user", method = RequestMethod.GET)
@GetMapping("/user")
public String getAllUser() {
System.out.println("查询操作 getAllUser user-->get请求方式");
return "success";
}
//@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
@GetMapping("/user/{id}")
public String getUserById(@PathVariable("id") Integer id) {
System.out.println("根据id查询用户信息操作 getUserById user/" + id + "-->get请求方式");
return "success";
}
//@RequestMapping(value = "/user", method = RequestMethod.POST)
@PostMapping("/user")
public String insertUser() {
System.out.println("添加用户信息 insertUser user-->post请求方式");
return "success";
}
//@RequestMapping(value = "/user", method = RequestMethod.PUT)
@PutMapping("/user")
public String updateUser() {
System.out.println("修改用户信息 updateUser user-->put请求方式");
return "success";
}
//@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
@DeleteMapping("/user/{id}")
public String deleteUser(@PathVariable("id") Integer id) {
System.out.println("删除用户信息 deleteUser user/" + id + "-->delete请求方式");
return "success";
}
}
重新部署一下Tomcat
发现都可以正常执行,这就是派生注解的应用