一:Spring Web MVC⼊⻔
Spring Web MVC 是⼀个 Web 框架,简称之为: Spring MVC
要真正的理解什么是 Spring MVC?我们⾸先要搞清楚什么是 MVC?
1.1 MVC 定义
MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构设计模式,它把软件系统分为模型、视图和控制器三个基本部分
- View(视图) 指在应⽤程序中专⻔⽤来与浏览器进⾏交互,展⽰数据的资源.
- Model(模型) 是应⽤程序的主体部分,⽤来处理程序中数据逻辑的部分.
- Controller(控制器)可以理解为⼀个分发器,⽤来决定对于视图发来的请求,需要⽤哪⼀个模型来处理,以及处理完后需要跳回到哪⼀个视图。即⽤来连接视图和模型
1.2 什么是Spring MVC
MVC 是⼀种架构设计模式, 也⼀种思想, ⽽ Spring MVC 是对 MVC 思想的具体实现. 除此之外, SpringMVC还是⼀个Web框架.
总结来说,Spring MVC 是⼀个实现了 MVC 模式的 Web 框架.所以, Spring MVC主要关注有两个点:
- MVC
- Web框架
其实, Spring MVC 我们在前⾯已经⽤过了, 在创建 Spring Boot 项⽬时,我们勾选的 Spring Web 框架其实就是 Spring MVC 框架:
Spring Boot 只是实现Spring MVC的其中⼀种方式,Spring Boot 可以添加很多依赖, 借助这些依赖实现不同的功能. Spring Boot 通过添加Spring WebMVC框架, 来实现web功能.
Spring在实现MVC时, 也结合⾃⾝项⽬的特点, 做了⼀些改变, 相对⽽⾔, 下⾯这个图或许更加合适⼀些.
1.3 学习Spring MVC
学习Spring MVC, 重点也就是学习如何通过浏览器和⽤⼾程序进⾏交互.
主要分以下三个⽅⾯:
- 建⽴连接:将⽤⼾(浏览器)和 Java 程序连接起来,也就是访问⼀个地址能够调⽤到我们的Spring 程序。
- 请求: ⽤⼾请求的时候会带⼀些参数,在程序中要想办法获取到参数, 所以请求这块主要是 获取参数的功能.
- 响应: 执⾏了业务逻辑之后,要把程序执⾏的结果返回给⽤⼾, 也就是响应.
1.3.1 项目准备
Spring MVC 项⽬创建和 Spring Boot 创建项⽬相同,在创建的时候选择 Spring Web 就相当于创建了Spring MVC 的项⽬.
创建项⽬时, 勾选上 Spring Web 模块即可,如下图所⽰:
1.4 建立连接 - 1
在 Spring MVC 中使⽤ @RequestMapping 来实现 URL 路由映射 ,也就是浏览器连接程序的作⽤
我们先来看看代码怎么写:
创建⼀个 UserController 类,实现⽤⼾通过浏览器和程序的交互,具体实现代码如下:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class UserController {
// 路由器规则注册
@RequestMapping("/sayHi")
public String sayHi(){
return "hello,Spring MVC";
}
}
⽅法名和路径名称⽆需⼀致:
接下来访问: http://127.0.0.1:8080/sayHi, 就可以看到程序返回的数据了
1.5 @RequestMapping 注解
@RequestMapping 是 Spring Web MVC 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路由映射的,表⽰服务收到请求时, 路径为 /sayHi 的请求就会调⽤ sayHi 这个⽅法的代码.
路由映射: 当⽤⼾访问⼀个 URL 时, 将⽤⼾的请求对应到程序中某个类的某个⽅法的过程就叫路由映射.
1.5.1 @RestController
既然 @RequestMapping 已经可以达到我们的⽬的了, 我们为什么还要加 @RestController呢?
我们把 @RestController 去掉, 再来访问⼀次:
可以看到, 程序报了404, 找不到该⻚⾯,这就是 @RestController 起到的作⽤.
一个项⽬中, 会有很多类, 每个类可能有很多的⽅法, Spring程序怎么知道要执⾏哪个⽅法呢?
Spring会对所有的类进⾏扫描, 如果类加了注解@RestController, Spring才会去看这个类⾥⾯的⽅法有没有加 @RequestMapping 这个注解, 当然他的作⽤不⽌这⼀点, 咱们先⽤, 后⾯再详细讲
1.5.2 @RequestMapping 使⽤
@RequestMapping 即可修饰类,也可以修饰⽅法 ,当修饰类和⽅法时,访问的地址是类路径 + ⽅法路径.
@RequestMapping标识⼀个类:设置映射请求的请求路径的初始信息
@RequestMapping标识⼀个⽅法:设置映射请求请求路径的具体信息
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/sayHi")
public String sayHi(){
return "hello,Spring MVC";
}
}
访问地址:http://127.0.0.1:8080/user/sayHi
注意:@RequestMapping 的URL 路径最前⾯加不加 / (斜杠)都可以, Spring程序启动时, 会进⾏判断, 如果前⾯没有加 / , Spring会拼接上⼀个 /(通常情况下, 我们加上 /)
@RequestMapping("user")
@RestController
public class UserController {
@RequestMapping("sayHi")
public String sayHi(){
return "hello,Spring MVC";
}
}
访问http://127.0.0.1:8080/user/sayHi, 依然可以正确响应.
@RequestMapping 的URL路径也可以是多层路径, 最终访问时, 依然是 类路径 + ⽅法路径
@RequestMapping("/user/m1")
@RestController
public class UserController {
@RequestMapping("/say/hi")
public String sayHi(){
return "hello,Spring MVC";
}
}
访问路径: http://127.0.0.1:8080/user/m1/say/hi
1.5.3@RequestMapping 是 GET 还是 POST 请求?
@RequestMapping 既⽀持Get请求, ⼜⽀持Post请求. 同理, 也⽀持其他的请求⽅式.
那如何指定GET或者POST类型呢?
1.5.3.1 指定GET/POST⽅法类型
我们可以显⽰的指定@RequestMapping 来接收POST的情况,如下所⽰:
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.ResponseBody;
@RestController
public class UserController {
@RequestMapping(value = "/getRequest",method= RequestMethod.POST)
public String sayHi(){
return "get request...";
}
}
1.6 Postman介绍
从上⾯的案例中, 也发现了⼀个新的问题, 就是我们测试后端⽅法时, 还需要去写前端代码. 这对我们来说, 是⼀件⿇烦⼜痛苦的事情.
随着互联⽹的发展, 也随着项⽬难度的增加, 企业也按照开发的功能, 把⼈员拆分成了不同的团队. 界⾯显⽰交给"前端开发⼯程师", 业务逻辑的实现交给 “后端开发⼯程师”.
后端开发⼯程师, 不要求也不需要掌握前端技能了.
那后端开发⼯程师, 如何测试⾃⼰的程序呢? – 使⽤专业的接⼝测试⼯具
1.6.1 创建请求
界⾯介绍:
1.6.2 传参
- 普通传参, 也就是通过查询字符串来传参
查询字符串就是请求的参数
- form-data(完整表⽰为: multipart/form-data)
表单提交的数据, 在 form 标签中加上 enctyped=“multipart/form-data” , 通常⽤于提交图⽚/⽂件. 对应 Content-Type: multipart/form-data
3. x-www-form-urlencoded
form表单, 对应 Content-Type: application/x-www-from-urlencoded
4. raw
可以上传任意格式的⽂本,可以上传text、json、xml、html等
1.7 请求 - 2
访问不同的路径, 就是发送不同的请求. 在发送请求时, 可能会带⼀些参数, 所以学习Spring的请求, 主要是学习如何传递参数到后端以及后端如何接收.
1.7.1 传递单个参数
接收单个参数, 在 Spring MVC 中直接⽤⽅法中的参数就可以,⽐如以下代码:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/m1")
public String method1(String name){
return "接收到参数name:"+ name;
}
}
咱们使⽤浏览器发送请求并传参:http://127.0.0.1:8080/param/m1?name=spring
可以看到, 后端程序正确拿到了name参数的值.
Spring MVC 会根据⽅法的参数名, 找到对应的参数, 赋值给⽅法
如果参数不⼀致, 是获取不到参数的.
⽐如请求URL: http://127.0.0.1:8080/param/m1?name1=spring
响应结果:
注意事项:
使⽤基本类型来接收参数时, 参数必须传(除boolean类型), 否则会报500错误
类型不匹配时, 会报400错误.
@RequestMapping("/m1/int")
public Object method1GetInt(int age){
return "接收到参数age:" + age;
}
- 正常传递参数
http://127.0.0.1:8080/param/m1/int?age=1
通过Fiddler观察请求和响应, HTTP响应状态码为200, Content-Type 为 text/html
2. 不传递age参数
http://127.0.0.1:8080/param/m1/int
通过Fiddler观察请求和响应, HTTP响应状态码为500
尝试观察程序的错误⽇志, 并解决
3. 传递参数类型不匹配
http://127.0.0.1:8080/param/m1/int?age=abc
通过Fiddler观察请求和响应, HTTP响应状态码为400
对于包装类型, 如果不传对应参数,Spring 接收到的数据则为null,所以企业开发中,对于参数可能为空的数据,建议使⽤包装类型
1.7.2 传递多个参数
如何接收多个参数呢?
和接收单个参数⼀样, 直接使⽤⽅法的参数接收即可. 使⽤多个形参.
@RequestMapping("/m2")
public Object method2(String name, String password) {
return "接收到参数name:" + name + ", password:" + password;
}
使⽤浏览器发送请求并传参: http://127.0.0.1:8080/param/m2?name=zhangsan&password=123456
可以看到, 后端程序正确拿到了name和password参数的值
当有多个参数时,前后端进⾏参数匹配时,是以参数的名称进⾏匹配的,因此参数的位置是不影响后端获取参数的结果.
⽐如访问: http://127.0.0.1:8080/param/m2?password=123456&name=zhangsan 同样可以拿到正确的结果
1.7.3 传递对象
如果参数⽐较多时, ⽅法声明就需要有很多形参. 并且后续每次新增⼀个参数, 也需要修改⽅法声明,我们不妨把这些参数封装为⼀个对象.
Spring MVC 也可以⾃动实现对象参数的赋值,⽐如 Person 对象:
**public class Person {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}**
传递对象代码实现:
@RequestMapping("/m3")
public Object method3(Person p){
return p.toString();
}
使⽤浏览器发送请求并传参: http://127.0.0.1:8080/param/m3?
id=5&name=zhangsan&password=123456
可以看到, 后端程序正确拿到了Person对象⾥各个属性的值
Spring 会根据参数名称⾃动绑定到对象的各个属性上, 如果某个属性未传递, 则赋值为null(基本类型则
赋值为默认初识值, ⽐如int类型的属性, 会被赋值为0)
1.7.4 后端参数重命名
某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不⼀致,⽐如前端传递了⼀个time 给后端,⽽后端是使⽤ createtime 字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使⽤ @RequestParam 来重命名前后端的参数值.
@RequestMapping("/m4")
public Object method_4(@RequestParam("time") String createtime) {
return "接收到参数createtime:" + createtime;
}
使⽤浏览器发送请求并传参: http://127.0.0.1:8080/param/m4?time=2023-09-12
可以看到, Spring可以正确的把浏览器传递的参数time绑定到了后端参数caretetime参数上
此时, 如果浏览器使⽤createtime进⾏参数传递呢?访问URL: http://127.0.0.1:8080/param/m4?createtime=2023-09-12
错误⽇志信息为:
控制台打印⽇志显⽰: 请求参数 ?time? 不存在
可以得出结论:
- 使⽤ @RequestParam 进⾏参数重命名时, 请求参数只能和 @RequestParam 声明的名称⼀致, 才能进⾏参数绑定和赋值.
- 使⽤ @RequestParam 进⾏参数重命名时, 参数就变成了必传参数.
⾮必传参数设置
如果我们的实际业务前端的参数是⼀个⾮必传的参数, 针对上述问题, 如何解决呢?
先来了解下参数必传的原因, 我们查看 @RequestParam 注解的实现细节就可以发现端倪,注解
实现如下:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n"
}
可以看到 required 的默认值为true, 表⽰含义就是: 该注解修饰的参数默认为必传
既然如此, 我们可以通过设置 @RequestParam 中的required=false 来避免不传递时报错,
具体实现如下:
@RequestMapping("/m4")
public Object method4(@RequestParam(value = "time", required = false) String cre
return "接收到参数createtime:" + createtime;
}
1.7.5 传递数组
Spring MVC 可以⾃动绑定数组参数的赋值
@RequestMapping("/m5")
public String method5(String[] arrayParam) {
return Arrays.toString(arrayParam);
}
使⽤浏览器发送请求并传参:
数组参数:请求参数名与形参数组名称相同且请求参数为多个, 后端定义数组类型形参即可接收参数http://127.0.0.1:8080/param/m5?arrayParam=zhangsan&arrayParam=lisi&arrayParam=wangwu
或者使⽤ http://127.0.0.1:8080/param/m6?listParam=zhangsan%2clisi%2cwangwu
或者使⽤ http://127.0.0.1:8080/param/m5?arrayParam=zhangsan,lisi,wangwu
浏览器响应结果:
1.7.6 传递集合
集合参数:和数组类似, 同⼀个请求参数名有为多个, 且需要使⽤ @RequestParam 绑定参数关系
请求⽅式和数组类似:
浏览器传参:‘
- ⽅式⼀:http://127.0.0.1:8080/param/m6listParam=zhangsan&listParam=lisi&listParam=wang
- wu ⽅式⼆:
http://127.0.0.1:8080/param/m6?listParam=zhangsan%2clisi%2cwangwu
**@RequestMapping("/m6")
public String method6(@RequestParam List<String> listParam){
return "size:"+listParam.size() + ",listParam:"+listParam;
}**
Postman传参测试:
1.7.7 传递JSON数据
JSON就是⼀种数据格式, 有⾃⼰的格式和语法, 使⽤⽂本表⽰⼀个对象或数组的信息, 因此JSON本质是字符串. 主要负责在不同的语⾔中数据传递和交换.
JSON 是⼀个字符串,其格式⾮常类似于 JavaScript 对象字⾯量的格式,我们先来看⼀段JSON数据:
{
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true,
"members": [
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": ["Radiation resistance", "Turning tiny", "Radiation blast"]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": ["Million tonne punch", "Damage resistance", "Superhuman strength"]
},
{
"name": "Eternal Flame",
"age": 1000000,
"secretIdentity": "Unknown",
"powers": ["Immortality", "Heat Immunity", "Inferno", "Teleportation"]
}
]
}
也可以压缩表⽰:
{"squadName":"Super hero squad","homeTown":"Metro City","formed":2016,"secretBaseSuper tower","active":true,"members":[{"name":"M Man","age":29,"secretIdentity":"Dan Jukes","powers":["Radiation resistance","Turning tiny","Radiation blast"]},{"name":"Madameppercut","age":39,"secretIdentity":"Jane Wilson","powers":["Million ton punch","Damage resistance","Superhuman reflexes"]},{"name":"Eternal Flame","age":1000000,"secretIdentity":"Unknown","powers":["Immortality","Heat Immunity","Inferno","Teleportation","Interdimensional travel"]}]}
JSON的语法:
- 数据在 键值对(Key/Value) 中
- 数据由逗号 , 分隔
- 对象⽤ {} 表⽰
- 数组⽤ [] 表⽰
- 值可以为对象, 也可以为数组, 数组中可以包含多个对象
JSON的两种结构
- 对象: ⼤括号 {} 保存的对象是⼀个⽆序的 键值对 集合. ⼀个对象以左括号 { 开始, 右括号 }结束。每个"键"后跟⼀个冒号 : ,键值对使⽤逗号 , 分隔
- 数组: 中括号 [] 保存的数组是值(value)的有序集合. ⼀个数组以左中括号 [ 开始, 右中括号 ] 结束,值之间使⽤逗号 , 分隔。
所以, 以下都是合法的JSON数据:
{"name":"admin","age":18}
["hello", 3.1415, "json"]
[{"name":"admin","age":18},{"name":"root","age":16},{"name":"张三","age":20}]
JSON优点:
- 简单易⽤: 语法简单,易于理解和编写,可以快速地进⾏数据交换
- 跨平台⽀持: JSON可以被多种编程语⾔解析和⽣成, 可以在不同的平台和语⾔之间进⾏数据交换和传输
- 轻量级: 相较于XML格式, JSON数据格式更加轻量级, 传输数据时占⽤带宽较⼩, 可以提⾼数据传输速度
- 易于扩展: JSON的数据结构灵活,⽀持嵌套对象和数组等复杂的数据结构,便于扩展和使⽤
- 安全性: JSON数据格式是⼀种纯⽂本格式,不包含可执⾏代码, 不会执⾏恶意代码,因此具有较⾼的安全性
基于以上特点, JSON在Web应⽤程序中被⼴泛使⽤, 如前后端数据交互、API接⼝数据传输等.
接收JSON对象, 需要使⽤ @RequestBody 注解
RequestBody: 请求正⽂,意思是这个注解作⽤在请求正⽂的数据绑定,请求参数必须在写在请求正⽂中
@RequestMapping(value = "/m7")
public Object method7(@RequestBody Person person) {
return person.toString();
}
使⽤Postman来发送json请求参数:
可以看到, 后端正确接收了,通过Fiddler观察⼀下请求参数:
1.7.8 获取URL中参数@PathVariable
path variable: 路径变量
和字⾯表达的意思⼀样, 这个注解主要作⽤在请求URL路径上的数据绑定,默认传递参数写在URL上,SpringMVC就可以获取到
@RequestMapping("/m8/{id}/{name}")
public String method8(@PathVariable Integer id, @PathVariable("name") String use
return "解析参数id:"+id+",name:"+userName;
}
使⽤浏览器发送请求:http://127.0.0.1:8080/param/m8/5/zhangsan
或者使⽤Postman发送请求
可以看到, 后端正确获取到了URL中的参数
参数对应关系如下:
如果⽅法参数名称和需要绑定的URL中的变量名称⼀致时, 可以简写, 不⽤给@PathVariable的属性赋值, 如上述例⼦中的id变量
如果⽅法参数名称和需要绑定的URL中的变量名称不⼀致时, 需要@PathVariable的属性value赋值,如上述例⼦中的userName变量.
1.7.9 上传⽂件@RequestPart
@RequestMapping("/m9")
public String getfile(@RequestPart("file") MultipartFile file) throws IOExceptio
//获取⽂件名称
String fileName = file.getOriginalFilename();
//⽂件上传到指定路径
file.transferTo(new File("D:/temp/" + file.getOriginalFilename()));
return "接收到⽂件名称为: "+fileName;
}
使⽤Postman发送请求:
观察 D:/temp 路径下, ⽂件是否上传成功:
1.8 获取Cookie
HTTP 协议⾃⾝是属于 “⽆状态” 协议,即默认情况下 HTTP 协议的客⼾端和服务器之间的这次通信, 和下次通信之间没有直接的联系.
但是实际开发中, 我们很多时候是需要知道请求之间的关联关系的.
上述图中的 “令牌” 通常就存储在 Cookie 字段中.
此时在服务器这边就需要记录"令牌"信息, 以及令牌对应的⽤⼾信息, 这个就是 Session 机制所做的⼯作.
1.8.1 理解Session
会话: 对话的意思
在计算机领域, 会话是⼀个客⼾与服务器之间的不中断的请求响应. 对客⼾的每个请求,服务器能够识别出请求来⾃于同⼀个客⼾. 当⼀个未知的客⼾向Web应⽤程序发送第⼀个请求时就开始了⼀个会话.当客⼾明确结束会话或服务器在⼀个时限内没有接受到客⼾的任何请求时,会话就结束了.
⽐如我们打客服电话,每次打客服电话, 是⼀个会话. 挂断电话, 会话就结束了,下次再打客服电话, ⼜是⼀个新的会话,如果我们⻓时间不说话, 没有新的请求, 会话也会结束.
服务器同⼀时刻收到的请求是很多的. 服务器需要清楚的区分每个请求是从属于哪个⽤⼾, 也就是属于哪个会话, 就需要在服务器这边记录每个会话以及与⽤⼾的信息的对应关系.
Session是服务器为了保存⽤⼾信息⽽创建的⼀个特殊的对象.
Session的本质就是⼀个 “哈希表”, 存储了⼀些键值对结构. Key 就是SessionID, Value 就是⽤⼾信息(⽤⼾信息可以根据需求灵活设计).
- 当⽤⼾登陆的时候, 服务器在 Session 中新增⼀个新记录, 并把 sessionId返回给客⼾端. (通过HTTP 响应中的 Set-Cookie 字段返回).
- 客⼾端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId. (通过 HTTP 请求中的Cookie 字段带上).
- 服务器收到请求之后, 根据请求中的 sessionId在 Session 信息中获取到对应的⽤⼾信息, 再进⾏后续操作.找不到则重新创建Session, 并把SessionID返回
Session 默认是保存在内存中的. 如果重启服务器则 Session 数据就会丢失.
1.8.2 Cookie 和 Session 的区别
- Cookie 是客⼾端保存⽤⼾信息的⼀种机制. Session 是服务器端保存⽤⼾信息的⼀种机制.
- Cookie 和 Session之间主要是通过 SessionId 关联起来的, SessionId 是 Cookie 和 Session之间的桥梁
- Cookie 和 Session 经常会在⼀起配合使⽤. 但是不是必须配合.
- 完全可以⽤ Cookie 来保存⼀些数据在客⼾端. 这些数据不⼀定是⽤⼾⾝份信息, 也不⼀定是SessionId
- Session 中的sessionId 也不需要⾮得通过 Cookie/Set-Cookie 传递, ⽐如通过URL传递.
1.8.3 获取Cookie
传统获取Cookie:
@RequestMapping("/m10")
public String method10(HttpServletRequest request,HttpServletResponse response){
// 获取所有 cookie 信息
Cookie[] cookies = request.getCookies();
//打印Cookie信息
StringBuilder builder = new StringBuilder();
if (cookies!=null){
for (Cookie ck:cookies) {
builder.append(ck.getName()+":"+ck.getValue());
}
}
return "Cookie信息:"+builder;
}
此时没有设置Cookie, 通过浏览器访问: http://127.0.0.1:8080/param/m10 ,得到Cookie为null
我们设置⼀下Cookie的值
再次访问:
从这个例⼦中, 也可以看出Cookie是可以伪造的, 也就是不安全的, 所以使⽤Cookie时, 后端需要进⾏Cookie校验
简洁获取Cookie
@RequestMapping("/getCookie")
public String cookie(@CookieValue("bite") String bite) {
return "bite:" + bite;
}
运⾏结果: http://127.0.0.1:8080/param/getCookie
1.9 获取Session
Session是服务器端的机制, 我们需要先存储, 才能再获取
Session 也是基于HttpServletRequest 来存储和获取的
Session存储
@RequestMapping("/setSess")
public String setsess(HttpServletRequest request) {
// 获取Session对象
HttpSession session = request.getSession();
if (session != null) {
session.setAttribute("username", "java");
}
return "session 存储成功";
}
这个代码中看不到 SessionId 这样的概念的. getSession 操作内部提取到请求中的Cookie ⾥的SessionId, 然后根据SessionId获取到对应的Session 对象, Session 对象⽤HttpSession来描述
获取Session有两种⽅式:
HttpSession getSession(boolean create);
HttpSession getSession();
HttpSession getSession(boolean create) : 参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null
HttpSession getSession(): 和getSession(true) 含义⼀样, 默认值为true.
void setAttribute(String name, Object value): 使⽤指定的名称绑定⼀个对象到该 session 会话
1.9.1 Session读取
读取 Session 可以使⽤ HttpServletRequest
@RequestMapping("/getSess")
public String sess(HttpServletRequest request) {
// 如果 session 不存在, 不会⾃动创建
HttpSession session = request.getSession(false);
String username = null;
if (session != null && session.getAttribute("username") != null) {
username = (String) session.getAttribute("username");
}
return "username:" + username;
}
运⾏:
先设置Session: http://127.0.0.1:8080/param/setSess
通过Fiddler观察Http请求和响应情况:
可以看到, Http响应中, 通过Set-Cookie告知客⼾端, 把SessionID存储在Cookie中
通过浏览器, 可以观察到运⾏结果:
获取Session: http://127.0.0.1:8080/param/getSess
通过Fiddler观察Http请求和响应
可以看到, Http请求时, 把SessionId通过Cookie传递到了服务器.
简洁获取 Session(1)
@RequestMapping("/getSess2")
public String sess2(@SessionAttribute(value = "username",required = false) String
return "username:"+username;
}
运⾏结果: http://127.0.0.1:8080/param/getSess2
简洁获取 Session(2)
通过Spring MVC内置对象HttpSession 来获取
@RequestMapping("/getSess3")
public String sess3(HttpSession session){
String username=(String)session.getAttribute("username");
return"username:"+username;
}
HttpSession session = request.getSession();Session 不存在的话, 会⾃动进⾏创建
运⾏结果: http://127.0.0.1:8080/param/getSess3
1.10 获取Header
传统获取 header:获取Header也是从 HttpServletRequest 中获取
@RequestMapping("/param10")
public String param10(HttpServletRequest request, HttpServletResponse response)
String userAgent = request.getHeader("User-Agent");
return name + ":"+userAgent;
}
运⾏结果: http://127.0.0.1:8080/param/getHeader
通过Fiddler观察, 获取的User-Agent是否正确
简洁获取 Header:
@RequestMapping("/header")
public String header(@RequestHeader("User-Agent") String userAgent) {
return "userAgent:"+userAgent;
}
运⾏结果: http://127.0.0.1:8080/param/getHeader2