假设有这样一个请求:
http://localhost:8080/springmvc/register?name=zhangsan&password=123&email=zhangsan@qq.com
- 在SpringMVC中应该如何
获取请求提交的数据
? - 在SpringMVC中又应该如何
获取请求头信息
? - 在SpringMVC中又应该如何
获取客户端提交的Cookie数据
?
一、准备
1.1 创建模块,添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.liming</groupId><artifactId>springmvc-004</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><properties><maven.compiler.source>21</maven.compiler.source><maven.compiler.target>21</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!--springmvc依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>6.1.4</version></dependency><!--logback依赖--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.5.3</version></dependency><!--servlet依赖--><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>6.0.0</version><scope>provided</scope></dependency><!--thymeleaf和spring6整合的依赖--><dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring6</artifactId><version>3.1.2.RELEASE</version></dependency></dependencies></project>
1.2 添加web支持
1.3 编写web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"version="6.0"><!--前端控制器--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--通过初始化参数来指定springmvc配置文件的路径和名字。--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param><!--在服务器启动的时候初始化DispatcherServlet,提高第一次访问的效率--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping></web-app>
1.4 创建UserController
@Controller
public class UserController {@RequestMapping("/")public String toRegisterPage(){return "register";}}
1.5 编写springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--组件扫描--><context:component-scan base-package="com.liming.controller"/><!--视图解析器--><bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver"><!--作用于视图渲染的过程中,可以设置视图渲染后输出时采用的编码字符集--><property name="characterEncoding" value="UTF-8"/><!--如果配置多个视图解析器,它来决定优先使用哪个视图解析器,它的值越小优先级越高--><property name="order" value="1"/><!--当 ThymeleafViewResolver 渲染模板时,会使用该模板引擎来解析、编译和渲染模板--><property name="templateEngine"><bean class="org.thymeleaf.spring6.SpringTemplateEngine"><!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器负责根据模板位置、模板资源名称、文件编码等信息,加载模板并对其进行解析--><property name="templateResolver"><bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver"><!--设置模板文件的位置(前缀)--><property name="prefix" value="/WEB-INF/templates/"/><!--设置模板文件后缀(后缀),Thymeleaf文件扩展名不一定是html,也可以是其他,例如txt,大部分都是html--><property name="suffix" value=".html"/><!--设置模板类型,例如:HTML,TEXT,JAVASCRIPT,CSS等--><property name="templateMode" value="HTML"/><!--用于模板文件在读取和解析过程中采用的编码字符集--><property name="characterEncoding" value="UTF-8"/></bean></property></bean></property></bean>
</beans>
1.6 编写register.html文件
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>用户注册</title>
</head>
<body>
<h3>用户注册</h3>
<hr>
</body>
</html>
1.7 部署测试
二、使用原生的Servlet API进行获取
原生的Servlet API指的是:
HttpServletRequest
。在SpringMVC当中,一个Controller类中的方法参数上如果有HttpServletRequest,SpringMVC会自动将当前请求对象
传递给这个参数,因此我们可以通过这个参数来获取请求提交的数据。
在 register.html 中准备一个注册的表单:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>用户注册</title>
</head>
<body>
<h3>用户注册</h3>
<hr>
<form th:action="@{/register}" method="post">用户名:<input type="text" name="username"><br>密码:<input type="password" name="password"><br>性别:男 <input type="radio" name="sex" value="1">女 <input type="radio" name="sex" value="0"><br>爱好:抽烟 <input type="checkbox" name="hobby" value="smoke">喝酒 <input type="checkbox" name="hobby" value="drink">烫头 <input type="checkbox" name="hobby" value="perm"><br>简介:<textarea rows="10" cols="60" name="intro"></textarea><br><input type="submit" value="注册">
</form>
</body>
</html>
接下来在控制器添加一个方法来处理这个注册的请求:
@PostMapping(value="/register")
public String register(HttpServletRequest request){// 通过当前请求对象获取提交的数据String username = request.getParameter("username");String password = request.getParameter("password");String sex = request.getParameter("sex");String[] hobbies = request.getParameterValues("hobby");String intro = request.getParameter("intro");System.out.println(username + "," + password + "," + sex + "," + Arrays.toString(hobbies) + "," + intro);return "success";
}
提供视图页面:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>注册成功</title>
</head>
<body>
<h1>注册成功</h1>
</body>
</html>
测试:
这样通过Servlet原生的API获取到提交的数据。但是这种方式不建议使用,因为方法的参数依赖Servlet原生API,Controller的测试将不能单独测试,必须依赖WEB服务器才能测试。另外,换句话说,如果在SpringMVC中使用了原生的Servlet,你为什么还要用SpringMVC框架呢!!!!!
三、使用RequestParam注解标注
3.1 RequestParam注解的基本使用
RequestParam注解作用:将
请求参数
与方法上的形参
映射。
@PostMapping(value = "/register")
public String register(@RequestParam(value="username")String a,@RequestParam(value="password")String b,@RequestParam(value="sex")String c,@RequestParam(value="hobby")String[] d,@RequestParam(name="intro")String e) {System.out.println(a);System.out.println(b);System.out.println(c);System.out.println(Arrays.toString(d));System.out.println(e);return "success";
}
注意:对于@RequestParam注解来说,属性有value和name,这两个属性的作用相同,都是用来指定提交数据的name
启动服务器测试:
注意: @RequestParam(value=“name”) 中value一定不要写错,写错就会出现以下问题:
测试结果:
3.2 RequestParam注解的required属性
required
属性用来设置该方法参数
是否为必须的
。默认情况下,这个参数为true
,表示方法参数是必需的。如果请求中缺少对应的参数,则会抛出异常。可以将其设置为false
,false表示不是必须的,如果请求中缺少对应的参数,则方法的参数为null。
测试,修改register方法,如下:
添加了一个 age 形参,没有指定 required 属性时,默认是true,表示必需的,但前端表单中没有年龄age,我们来看报错信息:参数age是必需的。没有提供这个请求参数,HTTP状态码 400
如果将 required 属性设置为 false。则该参数则不是必须的,如果请求参数仍然未提供时,我们来看结果:
通过测试得知,如果一个参数被设置为
不是必需的
,当没有提交对应的请求参数时,形参默认值null
。当然,如果请求参数中提供了age,则age为真实提交的数据。
3.3 RequestParam注解的defaultValue属性
defaultValue属性用来设置形参的默认值,当
没有提供对应的请求参数
或者请求参数的值是空字符串""
的时候,方法的形参会采用默认值。
当前端页面没有提交email的时候:
当前端页面提交的email是
空字符串
的时候也会走默认值
,只有当前端提交的email不是空字符串的时候才会接收用户输入的参数。
四、依靠控制器方法上的形参名来接收
@RequestParam
这个注解是可以省略的,如果方法形参的名字
和提交数据时的name相同
,则 @RequestParam 可以省略。但有一个前提:如果你采用的是Spring6+
版本,你需要在pom.xml文件中指定编译参数-parameter
,配置如下:
<build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.12.1</version><configuration><source>21</source><target>21</target><compilerArgs><arg>-parameters</arg></compilerArgs></configuration></plugin></plugins>
</build>
注意:如果你使用的是Spring5的版本,以上的配置是不需要的。
Controller中的方法只需要这样写:形参的名字必须和提交的数据的name一致!!!!!
@PostMapping(value="/register")
public String register(String username, String password, String sex, String[] hobby, String intro){System.out.println(username + "," + password + "," + sex + "," + Arrays.toString(hobby) + "," + intro);return "success";
}
注意:如果形参名和提交的数据的name不一致时会接受不到数据
五、使用POJO类/JavaBean接收请求参数
以上方式大家可以看到,当提交的数据非常多时,方法的形参个数会非常多,这不是很好的设计。在SpringMVC中也可以使用POJO类/JavaBean来接收请求参数。不过有一个非常重要的要求:
POJO类的属性名
必须和请求参数的参数名
保持一致。提供以下的JavaBean:
package com.liming.pojo;import lombok.Data;import java.io.Serializable;/*** @author LiMing* @version 1.0* @description: TODO* @date 2024/4/8 16:28*/
@Data
public class User implements Serializable {private Long id;private String username;private String password;private String sex;private String[] hobby;private String intro;}
在控制器方法的形参位置上使用javabean来接收请求参数:
@PostMapping("/register")
public String register(User user){System.out.println(user);return "success";
}
执行结果:
底层的实现原理:反射机制。先获取请求参数的名字,因为请求参数的名字就是JavaBean的属性名,通过这种方式给对应的属性赋值
我们来测试一下:当JavaBean的属性名和请求参数的参数名不一致时,会出现什么问题?(注意:getter和setter的方法名不修改,只修改属性名)
package com.powernode.springmvc.pojo;import java.util.Arrays;/*** @author LiMing* @version 1.0* @description: TODO* @date 2024/4/8 16:28*/
public class User {private Long id;private String uname;private String upwd;private String usex;private String[] uhobby;private String uintro;public User() {}public User(Long id, String username, String password, String sex, String[] hobby, String intro) {this.id = id;this.uname = username;this.upwd = password;this.usex = sex;this.uhobby = hobby;this.uintro = intro;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getUsername() {return uname;}public void setUsername(String username) {this.uname = username;}public String getPassword() {return upwd;}public void setPassword(String password) {this.upwd = password;}public String getSex() {return usex;}public void setSex(String sex) {this.usex = sex;}public String[] getHobby() {return uhobby;}public void setHobby(String[] hobby) {this.uhobby = hobby;}public String getIntro() {return uintro;}public void setIntro(String intro) {this.uintro = intro;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + uname + '\'' +", password='" + upwd + '\'' +", sex='" + usex + '\'' +", hobby=" + Arrays.toString(uhobby) +", intro='" + uintro + '\'' +'}';}
}
测试结果:
通过测试,我们得知:请求参数名
可以和JavaBean的属性名
不一致。
我们继续将其中一个属性的setter和getter方法名修改一下:
再次测试:
通过测试可以看到:username属性没有赋上值。可见请求参数是否可以赋值到JavaBean对应的属性上,不是取决于属性名,而是setter
方法名
六、RequestHeader注解
该注解的作用是:将
请求头信息
映射到方法的形参上
。
和RequestParam注解功能相似,RequestParam注解的作用:将请求参数
映射到方法的形参
上。
当然,对于RequestHeader注解来说,也有三个属性:value、required、defaultValue,和RequestParam一样,这里就不再赘述了。
测试:
@PostMapping("/register")
public String register(User user, @RequestHeader(value="Referer", required = false, defaultValue = "") String referer){System.out.println(user);System.out.println(referer);return "success";
}
执行结果:
七、CookieValue注解
该注解的作用:将
请求提交的Cookie数据
映射到方法形参
上
同样是有三个属性:value、required、defaultValue
前端页面中编写发送cookie的代码:
<script type="text/javascript">function sendCookie(){document.cookie = "id=123456789; expires=Thu, 18 Dec 2025 12:00:00 UTC; path=/";document.location = "/springmvc/register";}
</script>
<button onclick="sendCookie()">向服务器端发送Cookie</button>
后端UserController代码:
@GetMapping("/register")public String register(User user,@RequestHeader(value="Referer", required = false, defaultValue = "")String referer,@CookieValue(value="id", required = false, defaultValue = "2222222222")String id){System.out.println(user);System.out.println(referer);System.out.println(id);return "success";}
测试结果:
八、请求的中文乱码问题
8.1 get请求乱码
get
请求数据在URI
后面提交,这个乱码问题怎么解决呢?解决办法是找到CATALINA_HOME/config/server.xml
文件,找到其中配置端口号的标签,在该标签中添加URIEncoding="UTF-8"
。但是对于高版本的Tomcat服务器来说,是不需要设置的,例如Tomcat10,Tomcat9,有如下的默认配置,在默认情况下URIEncoding使用的就是UTF-8的编码方式。
但对于低版本的Tomcat服务器,例如:Tomcat8。URIEncoding的默认配置是
ISO-8859-1
,因此在Tomcat8中需要手动配置server.xml文件:
配置如下:
接下来,我们测试一下,在默认情况下,Tomcat10是否已经解决了get请求乱码问题:
<form th:action="@{/register}" method="get">用户名:<input type="text" name="username"><br>密码:<input type="password" name="password"><br>性别:男 <input type="radio" name="sex" value="1">女 <input type="radio" name="sex" value="0"><br>爱好:抽烟 <input type="checkbox" name="hobby" value="smoke">喝酒 <input type="checkbox" name="hobby" value="drink">烫头 <input type="checkbox" name="hobby" value="perm"><br>简介:<textarea rows="10" cols="60" name="intro"></textarea><br><input type="submit" value="注册">
</form>
注意,以上表单已经修改为get请求了。
@GetMapping("/register")
public String register(User user){System.out.println(user);return "success";
}
测试结果:
8.2 post请求乱码
post请求是解决请求体的中文乱码问题。解决办法大家都知道:
request.setCharacterEncoding("UTF-8");
同样,对于高版本的Tomcat10服务器来说,针对请求体中的字符编码也是配置好的,默认也是采用了UTF-8,中文乱码问题也解决了,在这个文件中配置的:
apache-tomcat-10.1.19\conf\web.xml
配置内容如下:
通过以上配置可以看到,Tomcat10对请求和响应都设置了默认的字符编码方式为UTF-8
注意:Tomcat9以及之前的版本,以上的配置是没有的
我们来测试一下,针对Tomcat10来说,SpringMVC会不会有乱码问题:
<form th:action="@{/register}" method="post">用户名:<input type="text" name="username"><br>密码:<input type="password" name="password"><br>性别:男 <input type="radio" name="sex" value="1">女 <input type="radio" name="sex" value="0"><br>爱好:抽烟 <input type="checkbox" name="hobby" value="smoke">喝酒 <input type="checkbox" name="hobby" value="drink">烫头 <input type="checkbox" name="hobby" value="perm"><br>简介:<textarea rows="10" cols="60" name="intro"></textarea><br><input type="submit" value="注册">
</form>
注意:以上表单已经修改为post请求
@PostMapping("/register")
public String register(User user, HttpServletRequest request) throws UnsupportedEncodingException {System.out.println(user);return "success";
}
测试结果:
通过测试可以看到在Tomcat10当中,默认SpringMVC,发送
POST
请求,是不会出现乱码问题的。
如果不是Tomcat10,则会出现乱码问题,我们来模拟一下乱码的产生,将apache-tomcat-10.1.19\conf\web.xml文件中的UTF-8配置修改为ISO-8859-1
:
一定要重启Tomcat10,新的配置才能生效,来测试一下是否存在乱码:
- 在SpringMVC中如何解决请求体的中文乱码问题呢?当然,还是使用
request.setCharacterEncoding("UTF-8")
- 使用它有一个前提条件,要想解决请求体乱码问题,以上代码必须在
request.getParameter("username")
执行之前执行才有效。 - 也就是说以上代码如果放在Controller的相关方法中执行是无效的,因为Controller的方法在执行之前 DispatcherServlet已经调用了
request.getParameter("username")
方法。因此在Controller方法中使用request.setCharacterEncoding("UTF-8")
无效
我们来测试一下:
<form th:action="@{/register}" method="post">用户名:<input type="text" name="username"><br>密码:<input type="password" name="password"><br>性别:男 <input type="radio" name="sex" value="1">女 <input type="radio" name="sex" value="0"><br>爱好:抽烟 <input type="checkbox" name="hobby" value="smoke">喝酒 <input type="checkbox" name="hobby" value="drink">烫头 <input type="checkbox" name="hobby" value="perm"><br>简介:<textarea rows="10" cols="60" name="intro"></textarea><br><input type="submit" value="注册">
</form>
注意:以上表单已经修改为post请求
@PostMapping("/register")
public String register(User user, HttpServletRequest request) throws UnsupportedEncodingException {request.setCharacterEncoding("UTF-8");System.out.println(user);return "success";
}
测试结果:
通过测试可以看到:在Controller当中调用request.setCharacterEncoding("UTF-8")
是无法解决POST乱码问题的。
那怎么办呢?怎么样才能在DispatcherServlet之前执行
request.setCharacterEncoding("UTF-8")
呢?没错,我相信大家想到了:过滤器Filter。过滤器Filter可以在Servlet执行之前执行。有同学又说了:监听器不行吗?不行。因为我们需要对每一次请求解决乱码,而监听器只在服务器启动阶段执行一次。因此这里解决每一次请求的乱码问题,应该使用过滤器Filter。并且,告诉大家一个好消息,SpringMVC已经将这个字符编码的过滤器提前写好了,我们直接配置好即可:CharacterEncodingFilter
,我们一起看一下它的源码:
最核心的方法是:
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {String encoding = getEncoding();if (encoding != null) {if (isForceRequestEncoding() || request.getCharacterEncoding() == null) {request.setCharacterEncoding(encoding);}if (isForceResponseEncoding()) {response.setCharacterEncoding(encoding);}}filterChain.doFilter(request, response);
}
分析以上核心方法得知该过滤器对请求和响应都设置了字符编码方式。
- 当
强行使用请求字符编码方式为true
时,或者请求对象的字符编码方式为null
时,设置请求的字符编码方式。 - 当
强行使用响应字符编码方式为true
时,设置响应的字符编码方式。
根据以上代码,可以得出以下配置信息,在web.xml文件中对过滤器进行如下配置:
<!--字符编码过滤器-->
<filter><filter-name>characterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param><param-name>forceRequestEncoding</param-name><param-value>true</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>
我们再来测试,重启Tomcat10,看看乱码是否能够解决?
注意:针对于我们当前的Tomcat10的配置来说,它有默认的字符集ISO-8859-1,因此以下在web.xml文件中的配置是不能缺少的:
<init-param><param-name>forceRequestEncoding</param-name><param-value>true</param-value>
</init-param>
如果缺少它,仍然是会存在乱码问题的。