Spring MVC 请求参数处理全解析

2.4 请求

访问不同的路径,就是发送不同的请求。在发送请求时,可能会带一些参数,所以学习 Spring 的请求,主要是学习如何传递参数到后端以及后端如何接收.传递参数,咱们主要是使用浏览器和 Postman 来模拟

后端开发人员无需过度关注如何传递参数,了解即可,实际开发中以 Postman 测试为主.比如餐厅的厨师,不关注用户是在店里下单,还是外卖平台下单,或者小程序下单,只需要知道如何接收订单,根据订单做出对应的菜肴就可以了.

2.4.1 传递单个参数

接收单个参数,在 Spring MVC 中直接用方法中的参数就可以,比如以下代码:

java 复制代码
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响应结果

那Postman中测试的结果

注意事项 :

使用基本类型来接收参数时,参数必须传(除boolean类型),否则会报500错误

类型不匹配时,会报400错误.

特殊情况:

1.不传参数:报了一个500的状态码,我们看后端日志

通过Fiddler观察请求和响应,HTTP响应状态码为500

⼀般看日志堆栈信息的首行,报错信息显示: int类型的参数'age',虽然为可选的,但由于被声明为基本类型而不能转换为空值.考虑将其声明为对应基本类型的包装类型.

按照错误信息解决错误即可 最开始学习时,会遇到各种各样的问题,我们要养成看错误⽇志的习惯,根据错误⽇志来解决问题

2.传递参数类型不匹配:状态码400

通过Fiddler观察请求和响应, HTTP响应状态码为400

注意:对于包装类型,如果不传对应参数,Spring接收到的数据则为null 所以企业开发中,对于参数可能为空的数据,建议使⽤包装类型

我们还可以用format进行代码的格式化
java 复制代码
@RequestMapping("/r4")
public String r3(String name, Integer age){
    return String.format("接受到参数:姓名[%s], age[%d]", name, age);
}

作用是接收前端传递的nameage两个参数,并将接收到的参数拼接成字符串返回给前端

Spring MVC 识别接口的核心是「请求路径 + 请求方式(可选)」,而非方法名
  1. Spring MVC 的映射核心 :Spring MVC 会将@RequestMapping路径、请求方式、请求参数、请求头 等信息组合成一个唯一的请求映射标识,只要这个标识不重复,即使方法名相同,也能被正确识别和调用。
  2. Java 方法的重载规则 :上述示例中同名的r3方法,参数列表不同(如(String, Integer)(String)),这本身符合 Java 的方法重载规则(方法名相同,参数列表不同),因此编译器也不会报错。
java 复制代码
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/param")
public class ParamController {

    // 路径1:/param/r4,方法名r3
    @RequestMapping("/r4")
    public String r3(String name, Integer age) {
        return String.format("接受到参数:姓名[%s], age[%d]", name, age);
    }

    // 路径2:/param/r5,方法名同样是r3(同名)
    @RequestMapping("/r5")
    public String r3(String address) {
        return "接收到参数:地址[" + address + "]";
    }

    // 甚至可以路径相同,但请求方式不同(如GET/POST),方法名也能相同
    @RequestMapping(value = "/r6", method = RequestMethod.GET)
    public String r3(String email) {
        return "GET请求:邮箱[" + email + "]";
    }

    @RequestMapping(value = "/r6", method = RequestMethod.POST)
    public String r3(String phone) {
        return "POST请求:手机号[" + phone + "]";
    }
}

2.4.2 传递多个参数

如何接收多个参数呢? 和接收单个参数⼀样,直接使用⽅法的参数接收即可.使用多个形参

java 复制代码
@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

当有多个参数时,前后端进⾏参数匹配时,是以参数的名称进⾏匹配的,因此参数的位置是不影响后端获取参数的结果.

比如访问:http://127.0.0.1:8080/param/m2?password=123456&name=zhangsan 同样可以拿到正 确的结果

可以看到,后端程序正确拿到了name和password参数的值

底层的逻辑是,先用这个特殊符号&进行分割,然后再从你的方法中看用到了啥,然后找到并且使用,所以这个顺序是可以不一样的但是名字必须是一样的

详细解释:

一、先明确:参数传递的 "载体" 与分割符&的作用

当前端通过URL 参数 (如/param/r4?name=张三&age=20)或表单提交 传递参数时,参数的格式是key=value的键值对,多个参数之间用&分隔,&的核心作用就是分割不同的键值对参数,这是 HTTP 协议规定的参数传递格式,和 Spring MVC 无关,但却是 Spring 处理参数的前提。

举个例子:name=张三&age=20&分割后,会得到两个独立的键值对:

  • name=张三(键:name,值:张三)
  • age=20(键:age,值:20)
二、Spring MVC 处理参数的核心步骤

Spring MVC 接收到分割后的键值对后,会按照以下逻辑匹配方法参数,这也是参数名必须一致、顺序可以任意的根本原因:

步骤 1:解析请求参数,生成 "参数键值对映射表"

Spring 会先把所有请求参数(不管是 URL 里的、表单里的)解析成一个 **Map<String, String>结构的映射表 **(键是参数名,值是参数值)。比如上面的例子,映射表就是:

java 复制代码
{
  "name": "张三",
  "age": "20"
}
步骤 2:根据方法的参数名,从映射表中 "按名取值"

Spring MVC 会获取后端方法的参数名(如r3(String name, Integer age)中的nameage),然后根据参数名去映射表中查找对应的值 ,找到后再进行类型转换(比如把字符串"20"转成Integer类型),最后把值传递给方法参数。

三、为什么 "顺序可以不一样,名字必须一样"?
1. 顺序可以不一样:因为参数是 "键值对",不是 "位置依赖"

HTTP 参数是键值对 结构,核心是key(参数名),而非参数在 URL 中的顺序。比如:

  • /param/r4?name=张三&age=20
  • /param/r4?age=20&name=张三

这两个请求的参数顺序不同,但解析后的映射表完全一样,Spring MVC 按名取值的结果也完全一样,所以方法接收的参数值不会受影响。

2. 名字必须一样:因为 Spring 是 "按名匹配",不是 "按位置匹配"

如果前端传递的参数名和方法参数名不一致,Spring MVC 在映射表中找不到对应的键,参数值就会变成null(除非用@RequestParam指定别名)。比如:前端请求:/param/r4?username=张三&age=20(参数名是username,不是name)后端方法:public String r3(String name, Integer age)此时name参数会是null,因为映射表中只有username,没有name这个键。

四、补充:如果想让参数名不一致怎么办?用@RequestParam指定别名

如果前端传递的参数名和后端方法参数名不一样,可以通过@RequestParam注解显式指定参数名的映射关系,这是实际开发中很常用的技巧:

java 复制代码
@RequestMapping("/r4")
public String r3(
    @RequestParam("username") String name, // 前端传username,后端用name接收
    Integer age
) {
    return String.format("接受到参数:姓名[%s], age[%d]", name, age);
}

此时前端请求/param/r4?username=张三&age=20,就能正确接收到name=张三了。

2.4.3 传递对象

如果参数比较多时,方法声明就需要有很多形参.并且后续每次新增⼀个参数,也需要修改方法声明. 我们不妨把这些参数封装为⼀个对象. Spring MVC也可以自动实现对象参数的赋值,比如Person对象

如果参数比较多时,方法声明就需要有很多形参.并且后续每次新增⼀个参数,也需要修改方法声明. 我们不妨把这些参数封装为⼀个对象.

Spring MVC也可以⾃动实现对象参数的赋值,比如Person对象

java 复制代码
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 + '\'' +
                '}';
    }
}

传递对象代码实现:

java 复制代码
    @RequestMapping("/m3")
    public Object method3(Person p){
        return p.toString();
    }

使用浏览器发送请求并传参:http://127.0.0.1:8080/param/m3?id=5&name=zhangsan&password=123456

Spring 会根据参数名称自动绑定到对象的各个属性上,如果某个属性未传递,则赋值为null(基本类型则 赋值为默认初识值,比如int类型的属性,会被赋值为0)

2.4.4 后端参数重命名(后端参数映射)

某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不一致,比如前端传递了一个 time 给后端,而后端是使用 createtime 字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使用 @RequestParam 来重命名前后端的参数值。

具体示例如下,后端实现代码:

java 复制代码
@RequestMapping("/r6")
public String r5(@RequestParam("sa") String name){
    return "接受到参数: " + name;
}

可以看到,Spring 可以正确的把浏览器传递的参数 time 绑定到了后端参数name 参数上此时,如果浏览器使用 name 进行参数传递呢?访问 URL:

fildder的使用查看

可以得出结论:

  1. 使用@RequestParam进行参数重命名时,请求参数只能和@RequestParam声明的名称一致,才能进行参数绑定和赋值.
  2. 使用@RequestParam进行参数重命名时,参数就变成了必传参数.

非必传参数设置如果我们的实际业务前端的参数是一个非必传的参数,针对上述问题,如何解决呢?先来了解下参数必传的原因,我们查看@RequestParam注解的实现细节就可以发现端倪,注解实现如下:

java 复制代码
@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 来避免不传递时报错,具体实现如下:

java 复制代码
@RequestMapping("/m4")
public Object method4(@RequestParam(value = "time", required = false) String createtime) {
    return "接收到参数createtime:" + createtime;
}

可以看到,添加 required=false 之后,time 前面也加了 key, 变成了 value = "time"注解属性赋值时,没有指明 key 的话,默认为 value 属性.如果需要有多个属性进行赋值时,需要写上 key

2.4.5 传递数组

Spring MVC可以⾃动绑定数组参数的赋值

后端实现代码:

java 复制代码
 @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/m5?arrayParam=zhangsan,lisi,wangwu浏览器响应结果:

可以看到后端对数组参数进行了正确的接收和响应.

或者使⽤Postman来发送请求

2.4.6 传递集合

2.4.6 传递集合集合参数:和数组类似,同一个请求参数名有多个,且需要使用 @RequestParam 绑定参数关系默认情况下,请求中参数名相同的多个值,是封装到数组。如果要封装到集合,要使用@RequestParam 绑定参数关系请求方式和数组类似:

浏览器传参:

方式一: http://127.0.0.1:8080/param/m6?listParam=zhangsan&listParam=lisi&listParam=wangwu

方式二::http://127.0.0.1:8080/param/m6?listParam=zhangsan%2clisi%2cwangwu

%2c 是逗号的转义编码,解码后的 url 为: http://127.0.0.1:8080/param/m6listParam=zhangsan,lisi,wangwu

后端接收代码:

java 复制代码
 @RequestMapping ("/m6")
 public String method6 (@RequestParam List<String> listParam){

 return "size:"+listParam.size() + ",listParam:"+listParam;
 }

2.4.7 传递 JSON 数据

JSON 概念

JSON: JavaScript Object Notation【JavaScript 对象表示法】JSON 是一种轻量级的数据交互格式。它基于 ECMAScript (欧洲计算机协会制定的 js 规范) 的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。-- 百度百科简单来说: JSON 就是一种数据格式,有自己的格式和语法,使用文本表示一个对象或数组的信息,因此JSON 本质是字符串。主要负责在不同的语言中数据传递和交换.

类似于:・国际通用语言 - 英语・中国 56 个民族不同地区的通用语言 - 普通话有自己的语法,其他语言也认识.

JSON 与 Javascript 的关系

没有关系,只是语法相似,js 开发者能更快的上手而已,但是他的语法本身比较简单,所以也很好学

JSON 语法

JSON 是一个字符串,其格式非常类似于 JavaScript 对象字面量的格式

我们先来看一段 JSON 数据

java 复制代码
{
  "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 reflexes"]
    },
    {
      "name": "Eternal Flame",
      "age": 1000000,
      "secretIdentity": "Unknown",
      "powers": ["Immortality", "Heat Immunity", "Inferno", "Teleportation", "Interdimensional travel"]
    }
  ]
}
  1. 小队基础信息

    • "squadName": "Super hero squad":小队名称(超级英雄小队)
    • "homeTown": "Metro City":家乡(大都会城)
    • "formed": 2016:成立年份(2016 年)
    • "secretBase": "Super tower":秘密基地(超级塔楼)
    • "active": true:是否活跃(是)
  2. **成员列表("members"是数组)**数组里包含 3 个英雄对象,每个对象描述一个英雄的信息:

    • 第一个英雄(Molecule Man)

      • "name": "Molecule Man":姓名
      • "age": 29:年龄
      • "secretIdentity": "Dan Jukes":秘密身份
      • "powers": [...]:超能力(抗辐射、变小、辐射爆炸)
    • 第二个英雄(Madame Uppercut)

      • "name": "Madame Uppercut":姓名
      • "age": 39:年龄
      • "secretIdentity": "Jane Wilson":秘密身份
      • "powers": [...]:超能力(百万吨拳、抗伤害、超人反应)
    • 第三个英雄(Eternal Flame)

      • "name": "Eternal Flame":姓名
      • "age": 1000000:年龄(100 万岁)
      • "secretIdentity": "Unknown":秘密身份(未知)
      • "powers": [...]:超能力(永生、耐热、地狱火、 teleportation、跨维度旅行)

可以压缩表示

java 复制代码
{"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 reflexes"]},{"name":"Eternal Flame","age":1000000,"secretIdentity":"Unknown","powers":["Immortality","Heat Immunity","Inferno","Teleportation","Interdimensional travel"]}]}
JSON 的语法:
  1. 数据在 键值对 (Key/Value) 中
  2. 数据由逗号 , 分隔
  3. 对象用 {} 表示
  4. 数组用 [] 表示
  5. 值可以为对象,也可以为数组,数组中可以包含多个对象
JSON 的两种结构
  1. 对象:大括号 {} 保存的对象是一个无序的 键值对 集合。一个对象以左括号 { 开始,右括号 } 结束。每个 "键" 后跟一个冒号 :,键值对使用逗号 , 分隔
  2. 数组:中括号 [] 保存的数组是值(value)的有序集合。一个数组以左中括号 [ 开始,右中括号 ] 结束,值之间使用逗号 , 分隔。

可 以使⽤在线JSON格式化⼯具来进⾏校验和书写:在线JSON校验格式化⼯具(BeJSON)

JSON 字符串和 Java 对象互转

JSON 本质上是一个字符串,通过文本来存储和描述数据

Spring MVC 框架也集成了 JSON 的转换工具,我们可以直接使用,来完成 JSON 字符串和 Java 对象的互转本质上是 jackson-databind 提供的功能,Spring MVC 框架中已经把该工具包引入了进来,咱们直接使用即可,如果脱离 Spring MVC 使用,需要引入相关依赖

java 复制代码
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.5</version>
</dependency>

JSON 的转换工具包有很多,jackson-databind 只是其中的一种.

java 复制代码
public class JSONUtils {
    private static ObjectMapper objectMapper = new ObjectMapper();

    public static void main(String[] args) throws JsonProcessingException {
        Person person = new Person();
        person.setId(5);
        person.setName("zhangsan");
        person.setPassword("123456");
        //对象转为JSON字符串
        String jsonStr = objectMapper.writeValueAsString(person);
        System.out.println("JSON字符串为:"+jsonStr);

        //JSON字符串转为对象
        Person p = objectMapper.readValue(jsonStr,Person.class);
        System.out.println("转换的对象 id:"+p.getId()+",name:"+p.getName()+",password:"+p.getPassword());
    }
}

使用 ObjectMapper 对象提供的两个方法,可以完成对象和 JSON 字符串的互转

  • writeValueAsString: 把对象转为 JSON 字符串
  • readValue: 把字符串转为对象
JSON 优点
  1. 简单易用:语法简单,易于理解和编写,可以快速地进行数据交换
  2. 跨平台支持: JSON 可以被多种编程语言解析和生成,可以在不同的平台和语言之间进行数据交换和传输
  3. 轻量级:相较于 XML 格式,JSON 数据格式更加轻量级,传输数据时占用带宽较小,可以提高数据传输速度
  4. 易于扩展: JSON 的数据结构灵活,支持嵌套对象和数组等复杂的数据结构,便于扩展和使用
  5. 安全性: JSON 数据格式是一种纯文本格式,不包含可执行代码,不会执行恶意代码,因此具有较高的安全性

基于以上特点,JSON 在 Web 应用程序中被广泛使用,如前后端数据交互、API 接口数据传输等.

维度 / 格式 Protobuf JSON XML
轻量级(评分) 1(最优) 2(中等) 3(最差)
可读性(评分) 3(最差) 2(中等) 1(最优)
典型适用场景 高性能 / 低空间要求的数据交互 通用数据交互(兼顾轻量与可读) 配置文件(可读性优先)
传递 JSON 对象

接收 JSON 对象,需要使用 @RequestBody 注解

RequestBody: 请求正文,意思是这个注解作用在请求正文的数据绑定,请求参数必须在写在请求正文中

后端实现:
java 复制代码
@RequestMapping(value = "/m7")
public Object method7(@RequestBody Person person) {
    return person.toString();
}

使用 Postman 来发送 json 请求参数:

可以看到,后端正确接收了 通过Fiddler观察⼀下请求参数

尝试去除掉 @RequestBody 试试

java 复制代码
@RequestMapping(value = "/m7")
public Object method7(Person person) {
return person.toString();
}

请求响应结果如下

后端未能成功给Person对象赋值.

2.4.8 获取 URL 中参数 @PathVariable

path variable: 路径变量和字面表达的意思一样,这个注解主要作用在请求 URL 路径上的数据绑定默认传递参数写在 URL 上,SpringMVC 就可以获取到

后端实现代码:

java 复制代码
@RequestMapping("/m8/{id}/{name}")
public String method8(@PathVariable Integer id, @PathVariable("name") String userName){
    return "解析参数id:"+id+",name:"+userName;
}

使用浏览器发送请求: http://127.0.0.1:8080/param/m8/5/zhangsan或者使用 Postman 发送请求

  1. @RequestMapping("/m8/{id}/{name}")

    • 这是 URL 路径的占位符写法{id}{name}是路径中的动态参数(占位符),表示访问/m8/xxx/xxx时,xxx会被解析为对应的参数值。
    • 示例请求 URL:http://127.0.0.1:8080/m8/100/spring(此时id=100name=spring)。
  1. public String method8(@PathVariable Integer id, @PathVariable("name") String userName)

    • @PathVariable:Spring MVC 的注解,用于绑定 URL 路径中的占位符参数到方法参数。
    • 第一个参数@PathVariable Integer id:路径中{id}的参数值会直接绑定到id(因为参数名和占位符名称一致)。
    • 第二个参数**@PathVariable("name") String userName** :路径中{name}的参数值,通过("name")指定占位符名称,绑定到方法参数userName(参数名和占位符名称不一致时,必须显式指定)。
  2. return "解析参数id:"+id+",name:"+userName;

    • 将接收到的iduserName拼接成字符串返回,作为响应结果。

URL 传参方式的验证思路说明:

那我们看他是不是用了url进行传参,就是把你给查询字符串(?后面的东西删除)

看看页面还正常使用么,如果可以就是使用了

2.4.9 上传文件 @RequestPart

后端代码实现:

java 复制代码
@RequestMapping("/m9")
public String getfile(@RequestPart("file") MultipartFile file) throws IOException {
    //获取文件名称
    String fileName = file.getOriginalFilename();
    //文件上传到指定路径
    file.transferTo(new File("D:/temp/" + file.getOriginalFilename()));

    return "接收到文件名称为: "+fileName;
}

使用 Postman 发送请求:

postman form-data

  • 含义:多部分表单数据 (对应 HTTP 的multipart/form-data格式)。
  • 用途:
    • 同时传文件普通参数(比如上传文件 + 传字符串参数);
    • 支持复杂数据结构(键值对、文件)。
  • 典型场景:文件上传接口(比如你之前的@RequestPart接口)。

使用 Postman 发送请求:

2.4.10 获取 Cookie/Session

回顾 CookieHTTP 协议

自身是属于 "无状态" 协议."无状态" 的含义指的是:

默认情况下 HTTP 协议的客户端和服务器之间的这次通信,和下次通信之间没有直接的联系.

但是实际开发中,我们很多时候是需要知道请求之间的关联关系的.

例如登陆网站成功后,第二次访问的时候服务器就能知道该请求是否是已经登陆过了.

上述图中的 "令牌" 通常就存储在 Cookie 字段中.比如去医院挂号

  1. 看病之前先挂号。挂号时候需要提供身份证号,同时得到了一张 "就诊卡", 这个就诊卡就相当于患者的 "令牌".
  2. 后续去各个科室进行检查,诊断,开药等操作,都不必再出示身份证了,只要凭就诊卡即可识别出当前患者的身份.
  3. 看完病了之后,不想要就诊卡了,就可以注销这个卡。此时患者的身份和就诊卡的关联就销毁了.(类似于网站的注销操作)
  4. 又来看病,可以办一张新的就诊卡,此时就得到了一个新的 "令牌"

此时在服务器这边就需要记录 "令牌" 信息,以及令牌对应的用户信息,这个就是 Session 机制所做的工作.

理解 Session

我们先来了解一下什么是会话.会话:对话的意思

在计算机领域,会话是一个客户与服务器之间的不中断的请求响应。对客户的每个请求,服务器能够识别出请求来自于同一个客户。当一个未知的客户向 Web 应用程序发送第一个请求时就开始了一个会话。当客户明确结束会话或服务器在一个时限内没有接受到客户的任何请求时,会话就结束了.

比如我们打客服

电话每次打客服电话,是一个会话。

挂断电话,会话就结束了下次再打客服电话,又是一个新的会话.

如果我们长时间不说话,没有新的请求,会话也会结束.

服务器同一时刻收到的请求是很多的。服务器需要清楚的区分每个请求是属于哪个用户,也就是属于哪个会话,就需要在服务器这边记录每个会话以及与用户的信息的对应关系.

Session 是服务器为了保存用户信息而创建的一个特殊的对象.

Session的本质就是⼀个"哈希表",存储了⼀些键值对结构.Key就是SessionID,Value就是⽤⼾信息(⽤ ⼾信息可以根据需求灵活设计).

SessionId 是由服务器生成的一个 "唯一性字符串", 从 Session 机制的角度来看,这个唯一性字符串称为 "SessionId". 但是站在整个登录流程中看待,也可以把这个唯一性字符串称为 "token".上述例子中的令牌 ID, 就可以看做是 SessionId, 只不过令牌除了 ID 之外,还会带一些其他信息,比如时间,签名等.

  1. 当用户登陆的时候,服务器在 Session 中新增一个新记录,并把 sessionId 返回给客户端.(通过 HTTP 响应中的 Set-Cookie 字段返回).
  2. 客户端后续再给服务器发送请求的时候,需要在请求中带上 sessionId.(通过 HTTP 请求中的 Cookie 字段带上).
  3. 服务器收到请求之后,根据请求中的 sessionId 在 Session 信息中获取到对应的用户信息,再进行后续操作。找不到则重新创建 Session, 并把 SessionID 返回

Session 默认是保存在内存中的.如果重启服务器则Session数据就会丢失

  • Cookie 是客户端保存用户信息的一种机制. Session 是服务端保存用户信息的一种机制.
  • Cookie 和 Session 之间主要是通过 SessionId 关联起来的,SessionId 是 Cookie 和 Session 之间的桥梁
  • Cookie 和 Session 经常会在一起配合使用。但是不是必须配合.
    • 完全可以用 Cookie 来保存一些数据在客户端。这些数据不一定是用户身份信息,也不一定是 SessionId
    • Session 中的 sessionId 也不需要非得通过 Cookie/Set-Cookie 传递,比如通过 URL 传递.

获取Cookie

传统获取Cookie
java 复制代码
   @RequestMapping("/getCookie")
    public String getCookie (HttpServletRequest request ,HttpServletresponse){

            Cookie[] cookies =  request.getCookies();
            StringBuilder stringBuilder = new StringBuilder();
if(cookies != null){
       for ( Cookie cookie :cookies) {
          stringBuilder.append("这个cookie 的名字是").append(cookie.getName()).append(cookie.getValue()).append("/n");
       }
       }else {
           stringBuilder.append("这个cookies 里面没有任何值  ");
       }

         return stringBuilder.toString();
   }

F12开发者工具也很好用

补充说明

Spring MVC 是基于 Servlet API 构建的原始 Web 框架,也是在 Servlet 的基础上实现的

HttpServletRequest , HttpServletResponse 是 Servlet 提供的两个类,是 Spring MVC 方法的内置对象,需要时直接在方法中添加声明即可.

HttpServletRequest 对象代表客户端的请求,当客户端通过 HTTP 协议访问服务器时,HTTP 请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息.

HttpServletResponse 对象代表服务器的响应.HTTP 响应的信息都在这个对象中,比如向客户端发送的数据,响应头,状态码等。通过这个对象提供的方法,可以获得服务器响应的所有内容

Spring MVC 在这两个对象的基础上进行了封装,给我们提供更加简单的使用方法.

使用注解的传递单个Cookie
java 复制代码
//使用注解传递的值
    @RequestMapping("/getTwoRequest")
    public String getCookie(@CookieValue("name") String cookie){
        return " 这个值是 " + cookie;
    }

获取 Session

Session 存储和获取

Session 是服务器端的机制,我们需要先存储,才能再获取Session 也是基于 HttpServletRequest 来存储和获取的

Session 存储
java 复制代码
@RequestMapping("/setSess")
public String setsess(HttpServletRequest request) {
    // 获取Session对象
    HttpSession session = request.getSession();
    if (session != null) {
        session.setAttribute("username", "java");
    }
    return "session 存储成功";
}

获取 Session 有两种方式

java 复制代码
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 会话

Session 读取

读取 Session 可以使用 HttpServletRequest

java 复制代码
@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;
}
  • Object getAttribute(String name):返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null

运行

先设置 Session: http://127.0.0.1:8080/param/setSess

先设置Session:http://127.0.0.1:8080/param/setSess

通过Fiddler观察Http请求和响应

可以看到,Http响应中,通过Set-Cookie告知客⼾端,把SessionID存储在Cookie中 通过浏览器,可以观察到运⾏结果:

可以看到,Http请求时,把SessionId通过Cookie传递到了服务器.

通过SpringMVC内置对象HttpSession来获
java 复制代码
@RequestMapping("/getSess2")
public String sess2(@SessionAttribute(value = "username",required = false)
String username) {
    return "username: "+username;
}

运行结果(getSess2)

地址:http://127.0.0.1:8080/param/getSess2页面显示:username: java

简洁获取 Session (2)

通过 Spring MVC 内置对象 HttpSession 来获取

代码 2

java 复制代码
@RequestMapping("/getSess3")
public String sess3(HttpSession session) {
    String username = (String)session.getAttribute("username");
    return "username: "+username;
}

补充说明

HttpSession session = request.getSession();Session 不存在的话,会自动进行创建

运行结果(getSess3)

地址:http://127.0.0.1:8080/param/getSess3页面显示:username: java

2.4.11 获取 Header

传统获取 header

获取 Header 也是从 HttpServletRequest 中获取

java 复制代码
@RequestMapping("/param10")
public String param10(HttpServletRequest request, HttpServletResponse response)
{
    String userAgent = request.getHeader("User-Agent");
    return name + ":" + userAgent;
}
  • 使用 HttpServletRequest 提供的 getHeader 方法来获取,参数对应 HTTP 请求报头的 "Key"
运行结果:
补充说明

通过 Fiddler 观察,获取的 User-Agent 是否正确

简洁获取 Header

java 复制代码
@RequestMapping("/header")
public String header(@RequestHeader("User-Agent") String userAgent) {
    return "userAgent: " + userAgent;
}
  • @RequestHeader注解的参数值为 HTTP 请求报头中的 "Key"

运行结果

地址:http://127.0.0.1:8080/param/getHeader2

相关推荐
winfield8212 小时前
Java 的静态代理和动态代理
java·代理模式
222you2 小时前
Java的Stream流
java·开发语言
kevinzeng2 小时前
Redis的IO多路复用
java·redis
2501_916766542 小时前
【SpringMVC】异常处理和拦截器
java·spring
不惑_2 小时前
在 Docker 中运行 Java JAR 包实战教程
java·docker·jar
一勺菠萝丶2 小时前
解决Java中IP地址访问HTTPS接口的SSL证书验证问题
java·tcp/ip·https
墨着染霜华2 小时前
IntelliJ IDEA 设置导出与导入完整指南(备份 / 迁移 / 团队共享)
java·ide·intellij-idea
浮游本尊2 小时前
Java学习第32天 - 性能优化与架构设计
java