一、认识Spring Web MVC
它是 Spring 框架的一部分,主要用于构建基于 MVC 设计模式的 Web 应用程序
1. MVC 定义
MVC 是 Model View Controller 的缩写,它是软件工程中的一种软件架构设计模式,它把软件系统分为模型、视图和控制器三个基本部分


2. 什么是Spring MVC?
MVC 是一种架构设计模式,也是一种思想,而 Spring MVC 是对 MVC 思想的具体实现,除此之外,Spring MVC还是一个Web框架,总结来说,Spring MVC 是一个实现了 MVC 模式的 Web 框架
所以,Spring MVC主要关注有两个点:
- MVC
- Web框架
Spring MVC 我们在前面已经用过了,在创建 Spring Boot 项目时,我们勾选的 Spring Web 框架,其实就是 Spring MVC 框架

二、 学习Spring MVC
主要分以下三个方面:
- 建立连接:将用户(浏览器)和 Java 程序连接起来,也就是访问一个地址能够调用到我们的Spring 程序
- 请求: 用户请求的时候会带一些参数,在程序中要想办法获取到参数,所以请求这块主要是获取参数的功能
- 响应: 执行了业务逻辑之后,要把程序执行的结果返回给用户,也就是响应
1. 项目准备
Spring MVC 项目创建和 Spring Boot 创建项目相同,在创建的时候选择 Spring Web 就相当于创建了Spring MVC 的项目
2. 建立连接
在 Spring MVC 中使用 @RequestMapping 来实现 URL 路由映射 ,也就是浏览器连接程序的作用


@RequestMapping 注解
1)@RequestMapping 是 Spring Web MVC 应用程序中最常被用到的注解之一,它是用来注册接口的路由映射的,表示服务收到请求时, 路径为 /request/r1 的请求就会调用 r1 这个方法的代码
@RequestMapping 既可修饰类,也可以修饰方法 ,当修饰类和方法时,访问的地址是类路径 + 方法路径
java
@RestController
@RequestMapping("/request")
public class RequestController {
//http://localhost:8080/request/r1?s1=hello
@RequestMapping("/r1")
public String r1(String s1) {
return "接受到的参数:" + s1;
}
**
2)@RequestMapping 是 GET 还是 POST 请求?
GET请求:
浏览器发送的请求类型都是get,通过以上案例,可以看出来 @RequestMapping 支持get请求
POST 请求:
我们通过form表单来构造请求
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request/r1" method="post">
<input type="submit" value="提交">
</form>
</body>
</html>

点击提交

从运行结果可以看出: @RequestMapping 既支持Get请求,又支持Post请求,同理,也支持其他的请求方式
3)指定GET/POST方法类型
java
@RequestMapping(value = "/hello2", method = {RequestMethod.GET, RequestMethod.POST})
public String hello2(){
return "hello, springmvc222";
}
3. 创建请求(使用postman)
1.普通传参,也就是通过查询字符串来传参

2.form-data

3.x-www-form-urlencoded

4.raw

4. 请求
访问不同的路径,就是发送不同的请求,在发送请求时,可能会带一些参数,所以学习Spring的请求,主要是学习如何传递参数到后端以及后端如何接收
1. 传递单个参数
java
@RequestMapping("/r3")
public String r3(Boolean flag) {
return "接受到的参数:" + flag;
}


注意:
使用基本类型来接收参数时,参数必须传(除boolean类型),否则会报500错误
类型不匹配时,会报400错误
2. 传递多个参数
java
@RequestMapping("/r4")
public String r4(String name, Integer age, Boolean flag) {
return "接受到的参数:" + name + " " + age + " " + flag;
}


3. 传递对象
java
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
private Integer gender;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
'}';
}
}
java
@RequestMapping("/r5")
public String r5(Person person) {
return "接受到的参数:" + person;
}


4. 后端参数重命名
某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不一致,比如前端传递了一个time 给后端,而后端是使用 createtime 字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使用 @RequestParam 来重命名前后端的参数值
java
@RequestMapping("/r6")
public String r6(@RequestParam(value = "sa", required = false) String name) {
return "接受到的参数:" + name;
}

5. 传递数组
java
@RequestMapping("/r7")
public String r7(String[] array) {
return "接受到的参数:" + Arrays.toString(array);
}

6. 传递集合
集合参数:和数组类似,同一个请求参数名有为多个,且需要使用@RequestParam 绑定参数关系
java
@RequestMapping("/r8")
public String r8(@RequestParam List<String> list) {
return "接受到的参数:" + list;
}

7. 传递JSON数据
JSON是一种轻量级的数据交换格式。它基于 JavaScript 编程语言的一个子集,但它是完全独立于语言的,被几乎所有现代编程语言所支持和使用,JSON 是一个字符串,其格式非常类似于 JavaScript 对象字面量的格式
JSON字符串和Java对象互转
java
void contextLoads() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
//对象转json
Person person = new Person();
person.setName("张三");
person.setAge(18);
person.setGender(1);
String json = mapper.writeValueAsString(person);
System.out.println(json);
//json转对象
mapper.readValue(json, Person.class);
System.out.println(person);
}

传递JSON对象
接收JSON对象, 需要使用 @RequestBody 注解
java
@RequestMapping("/r9")
public String r9(@RequestBody Person person) {
return "接受到的参数:" + person;
}


8. 获取URL中参数@PathVariable
path variable: 路径变量
和字面表达的意思一样,这个注解主要作用在请求URL路径上的数据绑定
java
@RequestMapping("/{acticleID}")
public String r10(@PathVariable("acticleID") Integer acticleID) {
return "接受到的参数:" + acticleID;
}

java
@RequestMapping("/{type}/{acticleID}")
public String r11(@PathVariable String type, @PathVariable Integer acticleID) {
return "接受到的参数:" + type + " " + acticleID;
}

如果方法参数名称和需要绑定的URL中的变量名称一致时,可以简写,不用给@PathVariable的属性赋值
9. 上传文件@RequestPart
java
@RequestMapping("/r12")
public String r12(MultipartFile file) throws IOException {
String name = file.getName();
String originalFilename = file.getOriginalFilename();
System.out.println(originalFilename);
String contentType = file.getContentType();
System.out.println(contentType);
file.transferTo(new File("D:\\课件" + originalFilename));
return "接受的文件: " + name;
}


10. 获取Cookie/Session
HTTP 协议自身是属于 "无状态" 协议
"无状态" 的含义指的是:
默认情况下 HTTP 协议的客户端和服务器之间的这次通信,和下次通信之间没有直接的联系
但是实际开发中,我们很多时候是需要知道请求之间的关联关系的。
例如登陆网站成功后,第二次访问的时候服务器就能知道该请求是否是已经登陆过了
Cookie 是服务器发送到用户浏览器并存储在客户端(浏览器)的一小块文本数据

Session 是一种在服务器端存储用户特定数据的机制

区别总结

Session 依赖于 Cookie 来工作。服务器会将一个名为 Session ID 的小令牌存储在 Cookie 中,发送给浏览器。浏览器在每次请求时带着这个 Session ID Cookie,服务器接收到 ID 后,就能在自己的 Session 存储区中找到对应的完整用户数据
获取Cookie
传统获取Cookie
java
@RequestMapping("/getCookie")
public String getCookie(HttpServletRequest request, HttpServletResponse response){
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName() + "=" + cookie.getValue());
}
}
return "获取成功";
}

此时没有设置Cookie,我们设置一下Cookie的值

查看日志

简洁获取Cookie
java
@RequestMapping("/getCookie2")
public String getCookie2(@CookieValue("flower") String flower){
System.out.println(flower);
return flower;
}

获取Session
Session 存储和获取
Session 是服务器端的机制,我们需要先存储,才能再获取
Session 也是基于HttpServletRequest 来存储和获取的
Session存储
java
@RequestMapping("/setSession")
public String setSession(HttpServletRequest request, String name){
HttpSession session = request.getSession();
session.setAttribute("name", "zhangsan");
session.setAttribute("age", "18");
session.setAttribute("userInfo", new Person("zhangsan", 18, 1));
return "设置成功" + name;
}
这个代码中看不到 SessionId 这样的概念的,getSession 操作内部提取到请求中的 Cookie 里的 SessionId,然后根据SessionId获取到对应的Session 对象,Session 对象用 HttpSession来描述


Session读取
java
@RequestMapping("/getSession")
public String getSession(HttpServletRequest request){
HttpSession session = request.getSession(false);
if (session != null) {
Object name = session.getAttribute("name");
Object age = session.getAttribute("age");
Object userInfo = session.getAttribute("userInfo");
System.out.println(name);
System.out.println(age);
System.out.println(userInfo);
return "获取成功";
}
return "获取失败";
}


简洁获取 Session(1)
java
@RequestMapping("/getSession2")
public String getSession2(@SessionAttribute("name") String name){
return name;
}

简洁获取 Session(2)
java
@RequestMapping("/getSession3")
public String getSession3(HttpSession session){
return (String) session.getAttribute("name");
}

11. 获取Header
传统获取 header
java
@RequestMapping("/getHeader")
public String getHeader(HttpServletRequest request){
return request.getHeader("user-agent");
}


简洁获取 Header
java
@RequestMapping("/getHeader2")
public String getHeader2(@RequestHeader("user-agent") String userAgent){
return userAgent;
}
@RequestHeader注解的参数值为HTTP请求报头中的Key


5. 响应
在我们前面的代码例子中,都已经设置了响应数据,Http 响应结果可以是数据,也可以是静态页面,可以针对响应设置状态码,Header信息等
1. 返回静态页面
创建前端页面 index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
我是一个index页面
</body>
</html>

java
@RequestMapping("/returnPage")
// @ResponseBody
public String returnPage(){
// List<Integer> list = List.of(1, 2, 3);
return "/index.html";
}

@Controller(返回页面) 等价于 @ResponseBody + @RestController(返回数据)
2. 返回数据@ResponseBody
java
@Controller
@RequestMapping("/returnPage")
@ResponseBody
public String returnPage(){
// List<Integer> list = List.of(1, 2, 3);
return "/index.html";
}

@ResponseBody 既是类注解,又是方法注解
如果作用在类上,表示该类的所有方法,返回的都是数据,如果作用在方法上,表示该方法返回的是数据
多个注解没有先后顺序
如果想返回视图的话,只需要把 @ResponseBody 去掉就可以了,也就是 @Controller
3. 返回HTML代码片段
java
@RequestMapping("/returnHtml")
@ResponseBody
public String returnHtml(){
return "<h1>hello world</h1>";
}


响应中的 Content-Type 常见取值有以下几种:
- text/html : body 数据格式是 HTML
- text/css : body 数据格式是 CSS
- application/javascript : body 数据格式是 JavaScript
- application/json : body 数据格式是 JSON
4. 返回JSON
java
@ResponseBody
@RequestMapping("/returnJson")
public Person returnJson(){
return new Person("张三", 18, 1);
}


java
@ResponseBody
@RequestMapping("/returnJson2")
public Map<String, String> returnJson2(){
Map<String, String> map = new HashMap<>();
map.put("name", "张三");
map.put("age", "18");
return map;
}


5. 设置状态码
Spring MVC会根据我们方法的返回结果自动设置响应状态码,程序员也可以手动指定状态码
java
@ResponseBody
@RequestMapping("/setStatus")
public String setStatus(HttpServletResponse response){
response.setStatus(400);
return "设置状态码成功";
}


6. 设置Header
Http响应报头也会向客户端传递一些附加信息,比如服务程序的名称,请求的资源已移动到新地址等,如:Content-Type, Local等
这些信息通过 @RequestMapping 注解的属性来实现
@RequestMapping 的源码:

- value: 指定映射的URL
- method: 指定请求的method类型,如GET,POST等
- consumes: 指定处理请求(request)的提交内容类型(Content-Type),例如application/json,text/html
- produces: 指定返回的内容类型,还可以同时设置返回值的字符编码
- Params: 指定request中必须包含某些参数值时,才让该方法处理
- headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求
7. 设置Content-Type
java
@ResponseBody
@RequestMapping(value = "/setContentType", produces = "application/json")
public String setContentType(){
return "{\"OK\":1}";
}


8. 设置其他Header
设置其他Header的话,需要使用Spring MVC的内置对象HttpServletResponse 提供的方法来进行设置
java
@ResponseBody
@RequestMapping("/setHeader")
public String setHeader(HttpServletResponse response){
response.setHeader("MyHeader","MyHeaderValue");
return "设置响应头成功";
}


三、综合性练习
1. 加法计算器
需求: 输入两个整数,点击"点击相加"按钮,显示计算结果
1. 准备工作
创建SpringBoot项目: 引入Spring Web依赖, 把前端页面放在项目中
2. 约定前后端交互接口
需求分析
加法计算器功能,对两个整数进行相加,需要客户端提供参与计算的两个数,服务端返回这两个整数计算的结果
接口定义
请求路径:calc/sum
请求方式:GET/POST
接口描述:计算两个整数相加
请求参数
| 参数名 | 类型 | 是否必须 | 备注 |
|---|---|---|---|
| num1 | Integer | 是 | 参与计算的第一个数 |
| num2 | Integer | 是 | 参与计算的第二个数 |
响应数据
Content-Type: text/html
3. 服务器代码
java
@RestController
@RequestMapping("/calc")
public class CalcController {
@RequestMapping("/sum")
public String sum(Integer num1,Integer num2){
Integer sum = num1+num2;
return "<h1>计算机计算结果: "+sum+"</h1>";
}
}
4. 调整前端页面代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="calc/sum" method="post">
<h1>计算器</h1>
数字1:<input name="num1" type="text"><br>
数字2:<input name="num2" type="text"><br>
<input type="submit" value=" 点击相加 ">
</form>
</body>
</html>
5. 运行测试


本期内容到此为止,喜欢的话请点个赞,谢谢观看!!!
