Spring MVC学习 | 获取请求参数

news2024/11/30 14:34:14

文章目录

  • 一、ServletAPI获取
  • 二、控制器方法形参获取
    • 2.1 不使用@RequestParam注解
    • 2.2 使用@RequestParam注解
      • 2.2.1 简介&使用
      • 2.2.2 相关注解
    • 2.3 实体类类型的形参
  • 三、中文乱码问题
    • 3.1 在tomcat中设置编码(了解)
    • 3.2 使用Spring MVC内部过滤器设置编码


学习视频🎥:https://www.bilibili.com/video/BV1Ry4y1574R

一、ServletAPI获取

💬概述:ServletAPI表示原生的Servlet接口和方法,Spring MVC可以使用原生的Servlet API来获取请求参数,直接通过request.getParameter("参数名")获取请求参数。但Spring MVC已经对原生的Servlet API进行过封装,所以不推荐直接使用原生的Servlet API获取请求参数

🔑使用:在控制器方法中添加HttpServletRequest类型的形参req,该形参就是原生servlet中的请求对象,封装了当前请求的请求报文对象,可以通过它直接调用getParameter("参数名")获取请求参数

@RequestMapping("/testParam")
public String showTestParam(HttpServletRequest req) {
    String username = req.getParameter("username");
    String pwd = req.getParameter("password");
   
    System.out.println("用户名:" + username);
    System.out.println("密码:" + pwd);
    return "success";
}

💡 因为控制器方法都是Spring MVC底层去调用,不用我们手动调用,所以Spring MVC底层会自动找到HttpServletRequest类型的形参,然后将当前请求中的请求对象赋值给形参req,因此req就表示当前请求(或者说封装了当前请求报文信息的请求对象)


二、控制器方法形参获取

💡 Spring MVC已经对获取请求参数的方式进行过封装,所以可以很方便地直接通过控制器方法形参来获取,建立形参与请求参数的映射关系有下列三种方式

2.1 不使用@RequestParam注解

💬概述:在不使用@RequestParam注解的情况下,可以直接在控制器方法的形参位置可添加与某个请求参数同名的形参,当浏览器发起的请求匹配到该控制器方法时,DispatcherServlet(或者说Spring MVC)会直接根据参数名将对应的参数值赋值给形参

❓ 关于形参名和形参类型

  1. 当形参名与请求参数名不一样时(不使用@RequestParam注解的情况下),Spring MVC就无法根据请求参数名找到对应的形参,即无法建立映射关系,从而获取不到对应的参数值(获取到null),但页面不会报错。比如请求参数设置为user_name,后台控制器方法形参设置为username,则后台打印出来的username值为null
  2. 浏览器发送过来的请求参数值都是String类型,而形参类型不是String类型,但形参名与其中的请求参数同名时,Spring MVC会尝试将该请求参数值转成形参对应的类型,如果类型转化成功就能成功赋值(如"123"–>int类型👌);但如果类型转化失败(如"Key"–>int类型❌),页面就会报400错误

🔑测试1.0——获取请求参数的单个值

① 创建测试超链接,带上两个请求参数(username、password)

 <a th:href="@{/testParam01(username='Key',password=123)}">测试形参获取请求参数</a>

② 创建对应的控制器方法,方法上添加两个形参,分别对应两个请求参数,同时保持形参名与请求参数名一致

@RequestMapping("/testParam01")
public String showTestParam01(String username, String password) {
    System.out.println("用户名:" + username);
    System.out.println("密码:" + pwd);
    return "success";
}

💡 用非rest方式书写的路径,在@RequestMappingvalue属性值无需中使用占位符,直接写/testParam01即可,即当前请求不用带上请求参数就能与控制器方法showTestParam()匹配上

🔑测试2.0——获取请求参数的多个值

① 创建测试表单,表单中添加一个复选框,实现一个多值的请求参数(hobby)

<form th:action="@{/testParam02}" method="post">
    用户名:<input type="text" name="username"><br/>
    爱好:<input type="checkbox" name="hobby" value="a">a
    <input type="checkbox" name="hobby" value="b">b
    <input type="checkbox" name="hobby" value="c">c <br/>
    <input type="submit" value="测试同名形参获取请求参数">
</form>

② 创建对应的控制器方法,方法中对应多值请求参数的形参有两种写法(形参名与请求参数名仍然要保持一致)

2.1 直接通过字符串类型形参获取全部请求参数值,获取的字符串结果中,多个请求参数值之间用,隔开(隔开后拼成的还是一个字符串)

@RequestMapping("/testParam02")
public String showTestParam02(String username, String hobby) {
    System.out.println("用户名:" + username);
    System.out.println("爱好:" + hobby);
    return "success";
}

/* 打印结果为:
	用户名:Key
	爱好:a,b,c
*/

2.2 通过字符串类型数组获取全部请求参数值,请求参数值为数组中的元素(跟req.getParameterValues()获取的结果类似)

@RequestMapping("/testParam02")
public String showTestParam02(String username, String[] hobby) {
    System.out.println("用户名:" + username);
    System.out.println("爱好:" + Arrays.toString(hobby));
    return "success";
}

/* 打印结果为:
	用户名:Key
	爱好:[a,b,c]
*/

2.2 使用@RequestParam注解

2.2.1 简介&使用

🔑添加的位置:在控制器方法的形参前添加

🔑作用:将请求参数与控制器方法的形参建立映射关系,注解的value值对应请求参数名,可以与形参名不一样

💡 在添加了@RequestParam注解后,Spring MVC就会根据注解的value值匹配对应的请求参数,从而匹配到注解所标识的形参,然后将请求参数值赋值给对应形参,所以在形参名与请求参数名不一致的情况同样能获取到对应的请求参数值

💡 测试

① 创建测试表单,带上请求参数(注意请求参数名设置为user_name、password)

<form th:action="@{/testParam03}" method="post">
    用户名:<input type="text" name="user_name"><br/>
    密码:<input type="password" name="password"><br/>
    <input type="submit" value="测试@RequestParam注解">
</form>

② 创建对应的控制器方法,方法上形参前添加@RequestParam注解,注解的value属性值对应请求参数名,而形参名(username、pwd)与请求参数名可以不一致

@RequestMapping("/testParam03")
public String showTestParam03(
    @RequestParam("user_name") String username,
    @RequestParam("password") String pwd) {

    System.out.println("用户名:" + username);
    System.out.println("密码:" + pwd);
    return "success";
}

🔑三个属性

① value:对应请求参数名,即指定给形参赋值的请求参数名

② required:设置是否必须传输value值对应的请求参数,默认为true

required的两个属性值

  1. true:表示必须传输对应的请求参数,即请求参数列表中必须要有value属性值对应的参数。如果没有传输对应参数(且没有设置defaultValue属性值),页面就会报400错误
  2. false:表示可以不传输对应的请求参数,即请求参数列表中可以没有value属性值对应的参数。如果没有传输对应参数,后台控制器方法对应形参值为null,即获取不到值,但页面不会报错

③ defaultValue:为对应形参指定默认值,即不管required值为true或者false,当value属性值对应的请求参数没有被传输或者值为空(空字符串"")时,控制器方法对应形参都能获取到值,该值就是defaultValue的属性值

2.2.2 相关注解

💡 下面两个注解使用方式与@RequestParam类似(请求参数、请求头信息和cookie数据都是键值对的形式),这里只简单介绍

@RequestHeader

  • 添加位置:控制器方法形参前
  • 作用:将请求头信息与控制器方法形参建立映射关系,可以将请求头中某个参数的参数值赋值给对应的形参
  • 三个属性(与@RequestParam注解的三个属性用法一样)
    Ⅰ. value:对应请求头中某个参数名
    Ⅱ. required:设置是否必须传输value值对应的请求头中某个参数,默认为true
    Ⅲ. defaultValue:为对应形参指定默认值

@CookieValue

  • 添加位置:控制器方法形参前
  • 作用:将cookie数据与控制器方法形参建立映射关系,可以将cookie数据值(value)赋值给对应的形参
  • 三个属性(与@RequestParam注解的三个属性用法一样)
    Ⅰ. value:对应cookie中某个数据名(key)
    Ⅱ. required:设置是否必须传输value值对应的cookie数据(key),默认为true
    Ⅲ. defaultValue:为对应形参指定默认值

2.3 实体类类型的形参

💬概述:在控制器方法中添加一个实体类类型的形参,当请求参数列表中有参数的名称与实体类成员变量的名称一样时,Spring MVC就会直接将对应请求参数值赋值给与其同名的实体类成员变量

❓ 关于实体类中成员变量类型:请求参数值都是String类型,而实体类中成员变量不是String类型时,但变量名与某个请求参数名一样时,此时Spring MVC会尝试将同名的请求参数值转成成员变量对应的类型,如果类型转化成功就能成功赋值;如果类型转化失败,页面就会报400错误(与普通类型的形参类型转化一样)

🔑测试

① 创建测试实体类User,成员变量有id、username、gender、age

public class User {
    private Integer id;
    private String username;
    private String gender;
    private Integer age;

    // 省略其他方法...
}

② 创建测试表单,表单中每一项分别对应实体类的每一个成员变量(除了id)

<form th:action="@{/testBean}" method="post">
    姓名:<input type="text" name="username"><br/>
    年龄:<input type="text" name="age"><br/>
    性别:<input type="radio" name="gender" value=""><input type="radio" name="gender" value=""><br/>
    <input type="submit" value="测试实体类类型形参">
</form>

③ 创建对应的控制器方法,方法中添加User类型的形参

@RequestMapping("/testBean")
public String showTestBean(User user) {
    System.out.println(user);
    return "success";
}

❓ 如果再添加其他形参

  1. 如果再添加一个User类型的形参userInfouserInfo对象中的成员变量同样也会被赋上对应的请求参数值,即打印useruserInfo的结果是一样的
  2. 如果再添加一个普通类型的形参,且与其中某个请求参数同名,则该形参仍然可以被赋上对应同名的请求参数的值。如上述测试中,控制器方法再添加一个形参String age的话,形参age依然能被赋上请求参数age的值

三、中文乱码问题

💡 对于原生的servlet API获取请求参数,还可以对获取到的参数进行重新编码——String username = New String(req.getParameter("username").getBytes("ISO8859-1"), "utf-8"),从而解决中文乱码问题。但太麻烦而且对于Spring MVC来说显然不适用

3.1 在tomcat中设置编码(了解)

🔑适合的请求方式只能解决GET请求时遇到中文乱码问题,对于POST请求还是无法解决,需要使用Spring MVC内部过滤器

🔑配置方式
① tomcat7.0之前:对于tomcat7.0之前的版本,GET方式提交的参数编码只支持ISO8859-1编码,所以需要我们手动修改——找到tomcat的server.xml 文件,在<Connector>标签中添加URIEncodeing="UTF-8"(或者设置为"GBK")

<Connector executor="tomcatThreadPool"
       port="8080" protocol="HTTP/1.1"
       connectionTimeout="20000"
       redirectPort="8443"
       <!-- 添加参数 -->
       URIEncoding="UTF-8" 
       useBodyEncodingForURI="true"/>

② tomcat8.0之后:对于tomcat8.0之后的版本,GET请求方式的默认解码已经是UTF-8。所以无需对server.xml文件中的<Connector>标签进行改动

3.2 使用Spring MVC内部过滤器设置编码

💬概述:在tomcat中配置编码只能解决GET请求遇到的乱码问题,而POST请求需要使用Spring MVC的内部编码过滤器CharacterEncodingFilter来设置编码方式

❓ 为什么使用过滤器设置编码:过滤器作为服务器三大组件之一,它的初始化时间在servlet之前,所以可以在前端控制器DispatcherServlet初始化前(获取请求参数前)设置编码方式,而且浏览器每次请求都会先经过过滤器,再到对应的处理请求方法,所以能够实现对每次请求都设置编码方式

❓ 为什么不使用servletAPI或监听器来设置编码

  1. 如果直接使用servletAPI来设置编码,即使用req.setCharacterEncoding("utf-8")来设置编码,则必须保证该设置在获取请求参数前。但在Spring MVC中DispatcherServlet在初始化时已经从浏览器请求中获取到请求参数,即使在控制器方法形参中添加HttpServletRequest对象,再通过setCharacterEncoding()设置编码已经是无用功,以为它是设置在获取请求参数后的。所以不使用该方法设置编码
  2. 监听器是服务器三大组件中初始化最早的一个,可以在servlet初始化前进行一些操作,但它仅在初始化的时候执行一次,后面销毁的时候再执行一次。所以在监听器初始化时设置编码的话,它只能在初始化时设置一次,当有多次请求时,设置就无效了

🔑配置方式:在web.xml中配置CharacterEncodingFilter过滤器,并设置相关属性

<!-- 配置Spring MVC内部编码过滤器,并设置相关属性 -->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

    <!-- 设置编码方式为utf-8 -->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <!-- 设置响应时也采用自定义的编码方式,如果只设置请求时的编码方式,可以不设置该属性 -->
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

❓ 关于CharacterEncodingFilter过滤器的配置

  1. 必须要设置encodingforceResponseEncoding两个属性,才能成功对请求和响应时设置编码方式。如果只对请求时设置编码,则可以只设置encoding一个属性,但如果要对响应时设置编码,就必须两个属性都设置。具体为什么可以从源码中找到原因(下面👇用🚩标记出原因)
  2. 如果web.xml还有其他过滤器的配置,则必须把CharacterEncodingFilter过滤器的配置放到所有过滤器最前面,因为过滤器的初始化顺序就是web.xml中的配置顺序,而设置编码的操作必须最先设置才有效。但不必放到servlet配置的前面,因为过滤器初始化本身就比servlet早,在web.xml中的配置顺序不会影响过滤器与servlet的初始化顺序

🔑分析源码

  • 打开CharacterEncodingFilter源码,可以看到过滤器有三个成员变量——encodingforceRequestEncodingforceResponseEncoding

    ① encoding:表示编码方式,没有初始化值,可以为null
    ② forceRequestEncoding:表示是否强制设置请求时的编码方式,初始值为false
    ③ forceResponseEncoding:表示是否强制设置响应时的编码方式,初始值为false
    encoding-filter01

  • 一般过滤器都有三个主要的方法——初始化方法init()、销毁方法destroy()以及执行过滤操作的方法doFilter(),主要看doFilter()。而CharacterEncodingFilter源码中的doFilterInternal()就是执行过滤器操作的方法。因为执行过滤操作中必须有放行操作,放行操作又需要过滤器链对象filterChain来执行,而doFilterInternal()方法的形参中正好有filterChain对象
    encoding-filter02

  • doFilterInternal()方法中,🚩可以看到给请求和响应设置编码的大前提是encoding变量不为null,而它初始值为null,所以我们必须在web.xml中给它设置对应的编码方式,才能实现设置编码的操作。

  • 设置请求编码方式还有一个小前提,即this.isForceRequestEncoding() || request.getCharacterEncoding() == null为true,该条件是与条件,只需满足其中一个就行,而我们之前没有设置过编码方式,所以request.getCharacterEncoding()获取的值必定为null,即条件request.getCharacterEncoding() == null必定为true,故即使变量forceRequestEncoding(对应this.isForceRequestEncoding())初值为false,也无需修改。🚩但响应编码方式的小前提只有this.isForceResponseEncoding()这一个条件,所以必须将forceResponseEncoding改成true才能设置响应时的编码方式
    encoding-filter03

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/84799.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【PortSwiggerのWeb Security Academy靶场】SQL Injection系列 9th

Subject Lab: Blind SQL injection with conditional responses Url: portswigger.net/web-securit… Mind Palace 在带着 cookies的后续访问网页时会找到出现的 Welcome back! 标志 > 无法回显 > 采用盲注的方式 0x01 确认注入点 0x02 爆破数据库名 # 测试informati…

qiankun 部署微前端-vue2(一)

自从前后端分离以来&#xff0c;一直都有个困惑&#xff0c;就是随着项目的功能的不断拓展&#xff0c;项目变得不断臃肿&#xff0c;每次打包编译&#xff0c;都要把整个项目编译&#xff0c;非常耗时。如果前端也能像后端一样&#xff0c;在项目搭建初期&#xff0c;有类似微…

C++11【包装器】

包装器&#x1f4d6;1. 为什么需要包装器&#x1f4d6;2. 如何使用包装器&#x1f4d6;3. bind函数&#x1f4d6;1. 为什么需要包装器 包装器也叫做适配器&#xff0c;C中的function本质是一个类模板&#xff0c;也是一个包装器. 为什么需要function呢&#xff1f; 我们来看…

ant-design-vue修改input组件样式

问题场景 不得不说ant-design-vue的样式是真的难改。。。今天尝试了很多种方案&#xff0c;都无疾而终。最终&#xff0c;通过全局scss文件引入的方式解决了。 几种方案&#xff1a; 直接行内样式&#xff0c;发现部分可以&#xff0c;部分不行将style 的scoped属性去掉&#…

非线性负载的主要分类及其特性

非线性负载的主要分类 通常而言&#xff0c;由线性元件原件组成的负载称为线性负载&#xff0c;线性负载的输出与输入呈线性关系&#xff0c;典型的线性负载如电阻、电容和电感等&#xff1b;而由非线性元件构成的负载为非线性负载&#xff0c;在正弦波电压供电时会产生非正弦…

Sentinel服务流控

Sentinel通过流量控制&#xff08;flow control&#xff09;以及熔断降级来保护系统资源 QPS超过阈值直接失败 流量控制&#xff08;flow control&#xff09;&#xff0c;其原理是监控应用流量的 QPS 或并发线程数等指标&#xff0c;当达到指定的阈值时对流量进行控制&#x…

如何使用ABAQUS对新能源动力电池进行Pack分析

电池Pack的仿真&#xff0c;按照系统层次&#xff0c;可从电芯、模组、Pack和整车逐级分析。电芯主要集中于机械性能的材料拟合、激光焊接以及电-化学-热-机耦合建模&#xff0c;模组主要集中于跌落、振动以及模组冷却&#xff0c;Pack主要集中于**、冲击和振动以及Pack热管理&…

Token Merging: Your ViT But Faster

论文&#xff1a;https://arxiv.org/pdf/2210.09461.pdf 代码&#xff1a; https://github.com/facebookresearch/ToMe 这篇论文写的很棒呀&#xff0c;以摘要为例&#xff0c;第一句话指明ToMe的作用&#xff08;提高ViT-based模型的训练和推理速度&#xff09;和特色&#x…

java面试强基(21)

什么是线程和进程? 何为进程? ​ 进程是程序的一次执行过程&#xff0c;是系统运行程序的基本单位&#xff0c;因此进程是动态的。系统运行一个程序即是一个进程从创建&#xff0c;运行到消亡的过程。 ​ 在 Java 中&#xff0c;当我们启动 main 函数时其实就是启动了一个 J…

提质增效!北京筑龙助力蒙牛集团采招数智化升级

近两年&#xff0c;全球经济下行压力加剧&#xff0c;市场形势波动使得集团企业面临着很大的经营压力。随着数字经济时代的到来&#xff0c;利用数字技术重构价值链、重组业务流程&#xff0c;或创建新的生态系统平台成为集团企业数字化转型的契机。 蒙牛电子采购招标平台作为…

WEB网页设计期末作业个人主页——基于HTML+CSS制作个人简介网站

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

现实中的项目范围变更

大多数招投标或签合同的项目(TOG或TOB),范围变更是极少的,一旦发起范围变更申请,这个流程是非常复杂的,而且会有负面影响,尤其是TOG的项目。 项目范围变更,往往会引起项目金额变更,核减、增加、不变都有可能。 TOG项目的范围变更,常是因为政策变了、上级要求等不可…

(附源码)Springboot网上购物平台 毕业设计 141422

Springboot网上购物平台的开发 摘 要 随着Internet的使用越来越广泛&#xff0c;在传统的商业模式中&#xff0c;对于日常各类商品&#xff0c;人们习惯于到各种商家店铺购买。然而在快节奏的新时代中&#xff0c;人们不一定能为购买各类商品腾出时间&#xff0c;更不会耐心挑…

Docker管理工具Portainer忘记admin登录密码怎么办?

Portainer官网解决方法链接 https://docs.portainer.io/v/ce-2.11/advanced/reset-admin 炒鸡详细步骤&#xff01; 1.查看所有容器,包括未运行的 docker ps -a 2.找到Portainer对应信息 3.停止Portainer容器 docker stop portainerid 我这里就应该是 docker stop 507566…

Unity VFX图表初级到中级教程

Unity VFX图表初级到中级教程 从 Unity 学习新的视觉效果工具并开始制作一些很棒的魔法效果 课程英文名&#xff1a;Unity VFX Graph - Beginner To Intermediate 此视频教程共4.5小时&#xff0c;中英双语字幕&#xff0c;画质清晰无水印&#xff0c;源码附件全 下载地址 …

【脚本项目源码】Python制作提升成功率90%的表白神器

前言 明天就是拥抱情人节&#xff0c;情侣们会在公开的场合拥抱&#xff0c;向世人宣告你俩的爱意&#xff0c;也让这个寒冷的冬天变得格外温馨。到了年底依然能热情拥抱&#xff0c;也见证了两人情意如昔。 今天小鱼就给大家带来就是的利用Python制作表白神器&#xff0c;记…

年末消费高峰,警惕“大牌平替”的陷阱

就在眼前的双十二&#xff0c;接踵而来的圣诞跨年与春节&#xff0c;买买买的黄金时段&#xff0c;作为消费者&#xff0c;少不了要接受各式营销广告的狂轰滥炸。如今纷繁复杂的消费市场&#xff0c;大家都擦亮了眼睛追求性价比&#xff0c;如果你也曾被“五分之一价格买到大牌…

【云计算与大数据计算】分布式处理CPU多核、MPI并行计算、Hadoop、Spark的简介(超详细)

一、CPU多核和POISX Thread 为了提高任务的计算处理能力&#xff0c;下面分别从硬件和软件层面研究新的计算处理能力 在硬件设备上,CPU 技术不断发展,出现了SMP(对称多处理器)和 NUMA(非一致 性内存访问)两种高速处理的 CPU 结构 在软件层面出现了多进程和多线程编程。进程…

【王道计算机网络笔记】网络层-网络层概述和编址

文章目录IP数据报格式IP数据报分片例题IPv4地址分类的IP地址网络地址转换NAT子网划分与子网掩码无分类编址CIDR主要任务是把分组从源端传到目的端&#xff0c;为分组交换网上的不同主机提供通信服务。网络层的传输单位是数据报&#xff0c;数据报是一个比较长的数据&#xff0c…

基于51单片机的智能热水器无线WiFi控制系统proteus仿真原理图程序

功能&#xff1a; 0.本项目采用STC89C52作为单片机系统的控制MCU 1.LCD1602液晶实时显示温度阈值、当前温度和定时时间 2.支持按键和红外遥控设置温度阈值和定时时间 3.通过传感器检测&#xff0c;判定当前值是否超过设定的阈值&#xff0c;然后对相关继电器进行控制 4.支持声光…