【Spring MVC】一篇文章带你吃透 SpringMVC 请求
文章摘要 本文深入探讨了SpringMVC请求参数处理的三种场景: 单个参数传递 对比Integer与int类型的差异:未传参时Integer默认为null,int会抛出500错误 建议优先使用包装类类型 多参数与对象传递 多参数通过名称匹配,与顺序无关 对象参数支持自动属性映射,无参构造方法会初始化默认值 参数重命名 使用@RequestParam注解解决前后端参数名不一致问题 支持设置必传校验

引言
上回说到,SpringMVC主要涉及三个方面——建立连接、请求、响应。建立连接的部分我们已经了解了,创建请求的工具我们也介绍了。下面,我们将详细的去学习请求与响应的具体实现!
文章目录
请求
访问不同的路径, 就是发送不同的请求. 在发送请求时, 可能会带⼀些参数, 所以学习Spring的请求, 主要是学习如何传递参数到后端以及后端如何接收。
传递单个参数
简单的参数传递上一节已经提到过了,诸如下面的代码,运行之后,在url中传递keyword的值,就可以将参数接收到了。
@RequestMapping("/r1")
public String r1(String keyword){
return "接收参数:" + keyword;
}
下面我们来看一些特殊一点的情况:
@RequestMapping("/r3")
public String r3(Integer number){
return "接收参数: " + number;
}
@RequestMapping("/r4")
public String r4(int number){
return "接收参数: " + number;
}
这两个方法在实际运行中你觉得有差别吗???
让咱们分不同情况来测试一下吧:
1.正常传递

两段代码都可以正常输出。
2.传递错误类型的数据

两个方法对应的程序输出错误信息状态码都是400,也就是用户端输入错误。
3.不输入数据


可以看到此时Integer类型的参数被自动赋值null,int类型对应的参数没有被赋值并且程序出现了状态码为500的错误,即服务器端错误,同时在日志中也能看到报错的信息。
SpringMVC 处理接口参数的核心流程是:解析请求参数 → 类型转换 → 赋值给方法参数,不同场景的表现差异都源于这个流程:
- 场景 1:正常传递正确类型数据
无论是 int 还是 Integer,SpringMVC 都会将请求中字符串格式的数字(比如 number=100)自动转换为对应的数值类型,赋值给参数,因此两者都能正常输出。 - 场景 2:传递错误类型数据(比如 number=abc)
SpringMVC 在「类型转换」阶段就会失败(无法把字符串 abc 转成数字),直接抛出「类型不匹配异常」,并将异常映射为 HTTP 400 状态码(客户端请求参数错误)。这个失败发生在「赋值」之前,因此 int 和 Integer 的表现一致。 - 场景 3:不传参数(核心差异)
- Integer 参数:SpringMVC 解析到没有该参数,会将参数值绑定为 null;而 Integer 作为包装类支持 null,因此参数赋值成功,接口正常执行,返回「接收参数: null」。
- int 参数:SpringMVC 同样尝试将 null 赋值给参数,但 int 作为基本类型无法存储 null,此时会直接抛出 NullPointerException(空指针异常)或 IllegalStateException(参数绑定失败)。
这个异常发生在服务器端的参数绑定环节,属于服务器内部处理错误,因此返回 HTTP 500 状态码(服务器内部错误)。
所以说,在日常使用中或者企业开发中,推荐使用包装类类型。
传递多个参数
@RequestMapping("/r2")
public String r2(String name,Integer age){
return "名字:" + name + "年龄:" + age;
}

传递多个参数时对应的关系是参数名,所以参数顺序无所谓,不会影响最终结果。
传递对象
如果参数比较多时, 方法声明就需要有很多形参. 并且后续每次新增⼀个参数, 也需要修改方法声明.我们不妨把这些参数封装为⼀个对象。
定义Userinfo类
package com.bite.spring.demo;
public class Userinfo {
public String name;
public int gender;
public int age;
public Userinfo(String name, int gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Userinfo{" +
"name='" + name + '\'' +
", gender=" + gender +
", age=" + age +
'}';
}
}
@RequestMapping("/r5")
public String r5(Userinfo userinfo){
return userinfo.toString();
}
Spring会根据参数名称自动将变量赋值给参数,还是与顺序无关。
不过由于对象的一些性质,这里有一个特殊情况:
如果我们加一个无参数的构造函数,此时,我们传递参数时故意不传递参数:
咱们刚刚说过,int类型变量不传递参数会产生服务器赋值异常,为什么这里没报错????
因为无参数的构造方法在构造对象时会自动赋值,包装类赋值null,基本类型赋值成0。
后端参数重命名(后端参数映射)
某些情况下,前端参数key和后端参数key可能不一样。比如前端为了不让用户直观的知道url中的具体信息,可能将keyword信息表示成q。但是我们后端写代码时还是更加习惯使用keyword这个变量名,此时就会用到@RequestParam来重命名前后端的值。
@RequestMapping("/r6")
public String r6(@RequestParam(value = "q",required = false)String keyword){
return "接收参数:" + keyword;
}
这里的required设置成true时表示务必传参,false时表示可以不传参。

注意,前端的映射是q,传参的时候不要搞错了哦!
传递数组
@RequestMapping("/r7")
public String r7(String[] arr){
return "接收参数: " + Arrays.toString(arr);
}


数组参数:请求参数名与形参数组名称相同且请求参数为多个, 后端定义数组类型形参即可接收参数
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
传递集合
传递集合的逻辑大致上与数组相同,但是在定义时需要用@RequestParam注解明确标识,因为Spring默认不会自动将「请求中的多个同名参数」封装为集合。

修改之后:
@RequestMapping("/r8")
public String r8(@RequestParam List<String> name){
return "接收参数: list = " + name;
}

传递JSON
JSON就是⼀种数据格式, 有自己的格式和语法,使用文本表示⼀个对象或数组的信息, 因此JSON本质是字符串. 主要负责在不同的语言中数据传递和交换。
语法:
1.数据在 键值对(Key/Value) 中
2. 数据由逗号 , 分隔
3. 对象⽤ {} 表⽰
4. 数组⽤ [] 表⽰
5. 值可以为对象, 也可以为数组, 数组中可以包含多个对象
接收JSON对象,需要使用@RequestBody注解。
@RequestMapping("/r9")
public String r9(@RequestBody Userinfo userinfo){
return "接收参数: " + userinfo.toString();
}

使用fiddler观察:
如果去掉@RequestBody注解,后端就无法正常给予变量赋值:
获取url中的参数@PathVariable
@RequestMapping("/article/{articleId}")
public String r10(@PathVariable Integer articleId){
return "获取文章ID:" + articleId;
}

@RequestMapping("/article/{type}/{articleId}")
public String r11(@PathVariable Integer articleId ,@PathVariable String type){
return "文章ID:" + articleId + "文章类型:" + type;
}

这种在今日头条这种新闻类网站会用到,大家可以去看看他们的url,大致可以猜到底层的代码逻辑。
上传文件@RequestPart
//上传文件
@RequestMapping("/r12")
public String r12(@RequestPart("file11") MultipartFile file) throws IOException {
System.out.println(file.getOriginalFilename());
//文件上传
file.transferTo(new File("D:\\cctalk"+ file.getOriginalFilename()));
return "文件上传成功";
}

获取Cookie/Session
Cookie是http部分我们就讲过的东西,不熟悉的大佬们可以回去看一下我这篇博客:
https://blog.csdn.net/2301_81614213/article/details/155863011?spm=1001.2014.3001.5501
Session:(会话)
在计算机领域, 会话是⼀个客户与服务器之间的不中断的请求响应. 对客户的每个请求,服务器能够识别出请求来自于同⼀个客户. 当⼀个未知的客户向Web应用程序发送第⼀个请求时就开始了⼀个会话.当客户明确结束会话或服务器在⼀个时限内没有接受到客户的任何请求时,会话就结束了.
服务器同⼀时刻收到的请求是很多的. 服务器需要清楚的区分每个请求是属于哪个用户, 也就是属于哪个会话, 就需要在服务器这边记录每个会话以及与用户的信息的对应关系.
Session是服务器为了保存用户信息⽽创建的⼀个特殊的对象。
Session的本质就是⼀个 “哈希表”, 存储了⼀些键值对结构. Key 就是SessionID, Value 就是用户信息(用户信息可以根据需求灵活设计).

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

Cookie 和 Session 的区别
- Cookie 是客户端保存用户信息的⼀种机制. Session 是服务器端保存用户信息的⼀种机制.
- Cookie 和 Session之间主要是通过 SessionId 关联起来的, SessionId 是 Cookie 和 Session 之间的桥梁
- Cookie 和 Session 经常会在⼀起配合使用. 但是不是必须配合.
- 完全可以⽤ Cookie 来保存⼀些数据在客户端. 这些数据不⼀定是用户⾝份信息, 也不⼀定是SessionId
- Session 中的sessionId 也不需要非得通过 Cookie/Set-Cookie 传递, 比如通过URL传递
代码实战:
首先来教大家如何发送一个带有cookie的请求:


send发送请求之后,在下面的cookies里面能找到自己设置的cookie表示成功。
接收cookie代码:
@RequestMapping("/r13")
public String r13(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
if(cookies!=null){
for (Cookie cookie:cookies){
System.out.println(cookie.getName() + ":" + cookie.getValue());
}
}
return "获取cookie成功";
}
使用HttpServletRequest实例化对象,虽然Servlet的使用已经是过去式,但是这个类很好用,通过这个类可以获得一个请求中的很多信息。
使用注解:
我们发送请求之后,日志中会有代码里面打印的数据。
@RequestMapping("/r14")
public String r14(@CookieValue("java") String java){
return "从cookie中获取java的值:" + java;
}

Session的处理
设置Session
@RequestMapping("/setSession")
public String setSession(HttpServletRequest request){
//从cookie中获取sessionId, 根据sessionId 获取Session对象
//如果sessionId不存在, 则创建
HttpSession session = request.getSession();
//默认存储在内存中
//登录的用户名称
session.setAttribute("userName", "zhangsan");
session.setAttribute("age", 17);
return "设置session成功";
}
getSession方法参数默认为true,从cookie中获取sessionId, 根据sessionId 获取Session对象,如果sessionId不存在, 则创建。
为false时,如果不存在sessionid,设置为null。
获取Session
//获取session
@RequestMapping("/getSession")
public String getSession(HttpServletRequest request){
//从cookie中获取sessionId, 根据sessionId 获取Session对象
HttpSession session = request.getSession(false);
//如果用户登录, session 有值, 未登录, session为null
if (session==null){
return "用户未登录";
}else {
//从session中获取登录用户的信息
String userName = (String)session.getAttribute("userName");
return "登录用户为: " + userName;
}
}
//获取session
@RequestMapping("/getSession2")
public String getSession2(HttpSession session){
//从session中获取登录用户的信息
String userName = (String)session.getAttribute("userName");
return "登录用户为: " + userName;
}
//获取session
@RequestMapping("/getSession3")
public String getSession3(@SessionAttribute("userName") String userName){
return "登录用户为: " + userName;
}



直接调用getSession方法的话,因为还没有设置,此时显示用户未登录。需要先使用setSession。建立了Session后,可以在Cookie里面看到SessionId。
对于SessionId:
✅ 不同客户端 → 不同服务器SessionId 完全不同。客户端和服务器都是独立的,两边的会话没有任何关联。
✅ 同一客户端 → 不同服务器SessionId 不同。每个服务器都会独立生成自己的 SessionId,客户端会为不同服务器保存不同的 Cookie。
✅ 不同客户端 → 同一服务器SessionId 不同。服务器会为每个新客户端分配唯一的 SessionId,确保会话隔离。
✅ 同一客户端不同时刻 → 同一服务器SessionId 分情况:
如果 Cookie 未过期、未被清除 → SessionId 相同
如果 Cookie 失效 / 被清除 → 服务器生成新的 SessionId,因此不同


就比如咱们的postman和浏览器就是两个不同的客户端,他们的SessionId就是不一样的。

如果使用fiddler抓包的话,可以看到设置SessionId时响应中有Set-cookie的部分。
获取Header
@RequestMapping("/getHeader")
public String getHeader(HttpServletRequest request){
String userAgent = request.getHeader("User-Agent");
return "从header中获取userAgent:"+ userAgent;
}
@RequestMapping("/getHeader2")
public String getHeader2(@RequestHeader("User-Agent") String userAgent){
return "从header中获取userAgent:"+ userAgent;
}


当然我们使用HttpServletRequest能获取到的请求头中Header肯定不止UserAgent这一个,肯定还有其他的部分。
更多推荐



所有评论(0)