Spring MVC 获取请求数据的四种方式,以及获取请求头数据,获取Cookie 的数据,设置Spring MVC 的字符集编码过滤器

1. Spring MVC 获取请求数据的四种方式,以及获取请求头数据,获取Cookie 的数据,设置Spring MVC 的字符集编码过滤器

文章目录

  • [1. Spring MVC 获取请求数据的四种方式,以及获取请求头数据,获取Cookie 的数据,设置Spring MVC 的字符集编码过滤器](#1. Spring MVC 获取请求数据的四种方式,以及获取请求头数据,获取Cookie 的数据,设置Spring MVC 的字符集编码过滤器)
  • [2. 准备工作:](#2. 准备工作:)
    • [2.1 创建模块,添加依赖](#2.1 创建模块,添加依赖)
    • [2.2 添加 web 支持](#2.2 添加 web 支持)
    • [2.3 编写 web.xml 文件](#2.3 编写 web.xml 文件)
    • [2.4 创建 UserController 类](#2.4 创建 UserController 类)
    • [2.5 编写 springmvc.xml](#2.5 编写 springmvc.xml)
    • [2.6 编写 register.html 文件视图](#2.6 编写 register.html 文件视图)
    • [2.7 部署测试](#2.7 部署测试)
  • [3. Spring MVC 获取请求数据的四种方式](#3. Spring MVC 获取请求数据的四种方式)
    • [3.1 第一种:在Spring MVC 框架中使用原生的 Servlet API 进行获取](#3.1 第一种:在Spring MVC 框架中使用原生的 Servlet API 进行获取)
    • [3.2 第二种方式:在 Spring MVC 框架中 使用 @RequestParam 注解标注,获取请求数据](#3.2 第二种方式:在 Spring MVC 框架中 使用 @RequestParam 注解标注,获取请求数据)
      • [3.2.1 补充:@RequestParam 注解的 required 属性](#3.2.1 补充:@RequestParam 注解的 required 属性)
      • [3.2.2 补充:@RequestParam注解的 defaultValue 属性](#3.2.2 补充:@RequestParam注解的 defaultValue 属性)
    • [3.3 第三种方式:依靠控制器方法上的形参名来接收(简单的说就是可以省略 @RequestParam注解也能获取到值 )](#3.3 第三种方式:依靠控制器方法上的形参名来接收(简单的说就是可以省略 @RequestParam注解也能获取到值 ))
    • [3.4 第四种方式:使用 POJO类/JavaBean 接收请求参数 (这是最常用的)](#3.4 第四种方式:使用 POJO类/JavaBean 接收请求参数 (这是最常用的))
  • [4. Spring MVC 中的 @RequestHeader注解 获取到"请求头"上的信息](#4. Spring MVC 中的 @RequestHeader注解 获取到“请求头”上的信息)
  • [5. Spring MVC 中的 @CookieValue注解 获取到 Cookie 数据当中的值](#5. Spring MVC 中的 @CookieValue注解 获取到 Cookie 数据当中的值)
  • [6. 补充:请求的中文乱码问题](#6. 补充:请求的中文乱码问题)
    • [6.1 get 请求中文乱码](#6.1 get 请求中文乱码)
    • [6.2 post 请求中文乱码](#6.2 post 请求中文乱码)
    • [6.3 中文乱码小结](#6.3 中文乱码小结)
  • [7. 总结:](#7. 总结:)
  • [8. 最后:](#8. 最后:)

2. 准备工作:

2.1 创建模块,添加依赖

xml 复制代码
<?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.rainbowsea</groupId>
    <artifactId>springmvc-004-blog</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </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>

2.2 添加 web 支持

先在 main 目录下,添加名为 webapp 的目录(文件夹),只能是这个 webapp 目录名,不可以是其他的。


2.3 编写 web.xml 文件

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_5_0.xsd"
         version="5.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>
<!--        除了 jsp 其他的路径都被获取到-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

2.4 创建 UserController 类

java 复制代码
package com.rainbowsea.springmvc.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller  // 交给 Spring IOC 容器管理
public class UserController {
    // 首页
    @RequestMapping("/")
    public String toRegisterPage(){
        return "register";
    }

}

2.5 编写 springmvc.xml

在 springmvc.xml 当中配置两个信息:

  • 组件扫描
  • 视图解析器
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.rainbowsea.springmvc.controller"></context:component-scan>

    <!--    视图解析器-->
    <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>

2.6 编写 register.html 文件视图

xml 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
</head>
<body>

</body>
</html>

2.7 部署测试


3. Spring MVC 获取请求数据的四种方式

3.1 第一种:在Spring MVC 框架中使用原生的 Servlet API 进行获取

原生的 Servlet API 指的是:HttpServletRequest

在 Spring MVC 当中,一个 Controller 类中的方法参数上如果有 :HttpServletRequest,Spring MVC 会自动将 前端请求对象 。传递给这个参数,因此我们可以通过这个参数来获取请求提交的数据。

其实本质上 Controller 类中的方法参数上的 HttpServletRequest (其他的:HttpServletResponse,HttpSession )也是可以的,它们本质上都是还是由 Tomcat 服务器创建的,是 Tomcat 服务器创建出来了,SpringMVC 框架会自动将 Tomcat 服务器创建 HttpServletRequest对象传递给处理器方法。我们直接在处理器方法中使用HttpServletRequest对象即可。同理的,HttpServletResponse,HttpSession 有需要的话,也可以采用这种方式注入

测试:在 register.html 中准备一个注册的表单:

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:thn="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
</head>
<body>
<!--注册页面-->
<form th:action="@{/user/reg}" 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="interest" value="Reading">
    敲代码<input type="checkbox" name="interest" value="Type the code">
    学习<input type="checkbox" name="interest" value="study">
    <br>
    简介
    <textarea cols="60" rows="10" name="intro"></textarea>
    <input type="submit" value="注册">
</form>

</body>
</html>

先测试这个页面是否可以正常打开,是否可以正常提交数据:

点击注册:F12的方式查看是否提交了数据:

通过测试得知:可以正常提交数据。

接下来在控制器添加一个方法来处理这个注册的请求:

java 复制代码
package com.rainbowsea.springmvc.controller;


import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.Arrays;

@Controller  // 交给 Spring IOC 容器管理
public class UserController {
    // 首页
    @RequestMapping("/")
    public String toRegisterPage(){
        return "register";
    }


    @RequestMapping(value = "/user/reg",method = RequestMethod.POST)
    public String register(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        // HttpServletResponse,HttpServletRequest,HttpSession 都属于原生 Servlet API
        System.out.println(request);
        System.out.println(response);
        System.out.println(session);

        // 获取请求提交的数据:
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String sex = request.getParameter("sex");
        String[] interests = request.getParameterValues("interest");
        String intro = request.getParameter("intro");


        System.out.println(username);
        System.out.println(password);
        System.out.println(sex);
        System.out.println(Arrays.toString(interests));//interest=Reading&interest=type the code&interest=study
        System.out.println(intro);

        return "ok";
    }

}

提供 ok 的视图页面:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OK</title>
</head>
<body>
<h1>Test OK</h1>

</body>
</html>

测试:

虽然这样通过 Servlet 原生的 API 获取到提交的数据,但是这种方式不建议使用,因为 方法的参数依赖 Servlet 原生 API ,Controller 的测试将不能单独测试,必须依赖 WEB 服务器才能测试,另外,换句话说,如果在 Spring MVC 中使用 了 原生的 Servlet ,你为什么还要用 Spring MVC 框架呢!!!

3.2 第二种方式:在 Spring MVC 框架中 使用 @RequestParam 注解标注,获取请求数据

RequestParam注解作用:将请求参数与方法上的形参映射

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.web.bind.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@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";
}

注意:对于 @RequestParam 注解来说,属性有 value 和 name,这两个属性的作用相同,都是用来指定提交数据的 name。

例如:发送请求时提交的数据是:name1=value1&name2=value2,则这个注解应该这样写:@RequestParam(value="name1")、@RequestParam(value="name2")

java 复制代码
@RequestParam(value = "不能随便写,要和表单提交的参数的名字,最好是复制过来的,变量名随意") name,value 都可以
    
java 复制代码
package com.rainbowsea.springmvc.controller;


import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Arrays;

@Controller  // 交给 Spring IOC 容器管理
public class UserController {
    // 首页
    @RequestMapping("/")
    public String toRegisterPage() {
        return "register";
    }


    @PostMapping(value = "/user/reg")
    public String register(
            //@RequestParam(value = "不能随便写,要和表单提交的参数的名字") name,value 都可以
            @RequestParam(value = "username") String username,  // username 不能随便写,最好是复制过来的,变量名随意
            //@RequestParam(value = "password") String password,
            @RequestParam(value = "password") String a, // 变量名随意
            @RequestParam(name = "sex") Integer sex,
            @RequestParam(value = "interest") String[] interest,  // SpringMVC也可以自动帮助我们做类型转换,从前端提交的是'0'/
            // '1'字符串,可以自动转换成 Integer类型
            @RequestParam(value = "intro") String intro
            //@RequestParam(value = "aqe" ,required = true) Integer age
    ) {

        System.out.println(username);
        //System.out.println(password);
        System.out.println(a);
        System.out.println(sex);
        System.out.println(Arrays.toString(interest));
        System.out.println(intro);

        return "ok";

    }

}

启动 Tomcat 服务器,看看能否成功获取到前端提交的信息。

一定要注意: @RequestParam(value="name2") 中 value 一定不要写错(一定要和前端的 name 保持一致),写错就会出现以下问题:400 错误

3.2.1 补充:@RequestParam 注解的 required 属性

required 属性用来设置该方法参数是否为必须的。

默认情况下,这个参数为 true ,表示方法参数是必须的,如果前端请求中缺少这个参数,则会抛异常,前端报 400 错误。可以将其设置为 false 表示不是必须的,如果前端请求中缺少对应的参数,则方法的参数值为 null。

测试,修改register方法,如下:

添加了一个 age 形参,没有指定 required 属性时,默认是 true,表示必需的,但前端表单中没有年龄age,我们来看报错信息:

错误信息告诉我们:参数age是必需的。没有提供这个请求参数,HTTP状态码 400

如果将 required 属性设置为 false。则该参数则不是必须的,如果请求参数仍然未提供时,我们来看结果:

java 复制代码
    @PostMapping(value = "/user/reg")
    public String register(
            //@RequestParam(value = "不能随便写,要和表单提交的参数的名字") name,value 都可以
            @RequestParam(value = "username") String username,  // username 不能随便写,最好是复制过来的,变量名随意
            @RequestParam(value = "password") String a, // 变量名随意
            @RequestParam(name = "sex") Integer sex,
            @RequestParam(value = "interest") String[] interest,  // SpringMVC也可以自动帮助我们做类型转换,从前端提交的是'0'/
            // '1'字符串,可以自动转换成 Integer类型
            @RequestParam(value = "intro") String intro,
            @RequestParam(value = "age",required = false) Integer age
    ) {

        System.out.println(username);
        //System.out.println(password);
        System.out.println(a);
        System.out.println(sex);
        System.out.println(Arrays.toString(interest));
        System.out.println(intro);

        System.out.println(age);

        return "ok";

    }

运行测试:

3.2.2 补充:@RequestParam注解的 defaultValue 属性

defaultValue属性用来设置形参的默认值,当没有提供对应的请求参数或者请求参数的值是空字符串""的时候,方法的形参会采用默认值。

当前端页面没有提交email的时候:

获取的是 defaultValue 属性的默认的值。

当前端页面提交的 email 是空字符串的时候:

当前端提交的 email 不是空字符串的时候:


3.3 第三种方式:依靠控制器方法上的形参名来接收(简单的说就是可以省略 @RequestParam注解也能获取到值 )

@RequestParam这个注解是可以省略的,如果方法形参的名字和提交数据时的 name 相同,则 @RequestParam 可以省略。

但有一个前提:如果你采用的是Spring6+版本,你需要在 pom.xml 文件中指定编译参数'-parameter',配置如下:

xml 复制代码
<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一致!!!!!

java 复制代码
package com.rainbowsea.springmvc.controller;


import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Arrays;

@Controller  // 交给 Spring IOC 容器管理
public class UserController {
    // 首页
    @RequestMapping("/")
    public String toRegisterPage() {
        return "register";
    }


    @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 "ok";
    }

}

测试结果:

如果形参名和提交的数据的 name 不一致时:为 null

另外,还有一点,对于提交的 hobby 数据,也可以采用 String 来接收,不一定使用数组方式:

java 复制代码
@PostMapping(value="/register")
public String register(String username, String password, String sex, String hobby, String intro){
    System.out.println(username + "," + password + "," + sex + "," + hobby + "," + intro);
    return "ok";
}

根据输出结果可以看到多个hobby是采用","进行连接的。

3.4 第四种方式:使用 POJO类/JavaBean 接收请求参数 (这是最常用的)

以上方式大家可以看到,当提交的数据非常多时,方法的形参个数会非常多,这不是很好的设计。

在 Spring MVC 中也可以使用 POJO类 / JavaBean 类来接收请求参数。不过有一个非常重要的要求:POJO类的属性名 必须和 请求(前端)参数的参数名保持一致。 提供以下的 Java Bean/POJO类

java 复制代码
package com.rainbowsea.springmvc.pojo;

import java.util.Arrays;

public class User {
    private Long id;
    private String a;  // 本质上调用的是 对应的 setXXX 方法进行赋值操作的
    private String password;
    private Integer sex;
    private String[] interest;
    private String intro;
    private Integer age;


    public User() {
    }


    public User(Long id, String username, String password, Integer sex, String[] interest, String intro, Integer age) {
        this.id = id;
        this.a = username;
        this.password = password;
        this.sex = sex;
        this.interest = interest;
        this.intro = intro;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + a + '\'' +
                ", password='" + password + '\'' +
                ", sex=" + sex +
                ", interest=" + Arrays.toString(interest) +
                ", intro='" + intro + '\'' +
                ", age=" + age +
                '}';
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return a;
    }

    public void setUsername(String username) {
        this.a = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public String[] getInterest() {
        return interest;
    }

    public void setInterest(String[] interest) {
        this.interest = interest;
    }

    public String getIntro() {
        return intro;
    }

    public void setIntro(String intro) {
        this.intro = intro;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

前端添加上 age 属性,

在控制器方法的形参位置上使用 javabean/POJO类 来接收请求参数:

java 复制代码
import com.rainbowsea.springmvc.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Arrays;

@Controller  // 交给 Spring IOC 容器管理
public class UserController {
    // 首页
    @RequestMapping("/")
    public String toRegisterPage() {
        return "register";
    }


    @PostMapping(value = "/user/reg")
    public String register(User user) {
        System.out.println(user);
        return "ok";
    }
}

执行结果:

底层的实现原理:反射机制。先获取请求参数的名字,因为请求参数的名字就是JavaBean的属性名,通过这种方式给对应的属性赋值

其实本质上::JavaBean的属性名和请求参数的参数名不一致时,会出现什么问题?(注意:getter和setter的方法名不修改,只修改属性名 )是可以成功赋值的,主要是对应的 get()和 set()方法名要和请求(前端)参数保持一致就行,就可以通过反射机制进行一个调用

java 复制代码
底层是原理:反射机制
> 不过,使用着这种方式的前提是,POJO类的属性名必须和请求参数的参数名保持一致
> 实现原理?
>   前端提交了一个请求,参数名是:username,那么要求POJO类当中必须有一个属性名也叫做:username
>   Class Clazz = Class.forName("com.rainbowsea.springmvc.pojo.User");
>   User user = (User)clazz.newInstance();
>   String fieldName = "username"
>   String setMethodName = "setUsername"
>   Method setMethod = clazz.getDeclaredMethod(setMethodName,...)
>   setMethod.invoke(user,"zhaoliu")
> 
> 重点:底层通过反射机制调用set方法给属性赋值,所以 set 方法的方法名非常重要
> 如果前端提交了参数是,username=zhangsan
> 那么必须保证POJO类当中有一个方法名叫做:setUserName
> 如果前端提交参数是:email=zhangsna@rainbowsea.com
> 那么必须保证POJO类当中有一个方法名叫做:setEmail
> 如果没有对应的set方法,将无法给对应的属性赋值。

我们继续将其中一个属性的setter和getter方法名修改一下:和前端的 name 不一致。

再次测试:

通过测试可以看到:username属性没有赋上值。可见请求参数是否可以赋值到JavaBean对应的属性上,不是取决于属性名,而是 setter方法名

4. Spring MVC 中的 @RequestHeader注解 获取到"请求头"上的信息

@RequestHeader 该注解的作用是:将请求头信息映射到方法的形参上

和RequestParam注解功能相似,RequestParam注解的作用:将请求参数映射到方法的形参上。

当然,对于RequestHeader注解来说,也有三个属性:value、required、defaultValue,和RequestParam一样,这里就不再赘述了。

java 复制代码
 public String register(User user,
                           @RequestHeader(value = "请求头的 name ", required = false, defaultValue = "") String referer,

测试:

java 复制代码
import com.rainbowsea.springmvc.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Arrays;

@Controller  // 交给 Spring IOC 容器管理
public class UserController {
    // 首页
    @RequestMapping("/")
    public String toRegisterPage() {
        return "register";
    }

    @PostMapping("/user/reg")
    public String register(User user,
                           @RequestHeader(value = "Referer", required = false, defaultValue = "") String referer,
                           @RequestHeader(value = "Host", required = false, defaultValue = "") String host) {

        System.out.println(user);
        System.out.println("referer: " + referer); // Referer: http://localhost:8080/springmvc/
        System.out.println("host: " + host);  // Host: localhost:8080
        return "ok";

    }

}

执行结果:


@CookieValue 该注解的作用:将请求提交的Cookie数据映射到方法形参

同样是有三个属性:value、required、defaultValue,作用也是和 @RequestParam 注解的是一样的,这里就不再重复说明了。

java 复制代码
@CookieValue(value = "cookie的名字") String id

前端页面中编写发送 cookie 的代码:

html 复制代码
<script type="text/javascript">
    function sendCookie(){
        document.cookie = "id=123456789; expires=Thu, 18 Dec 2025 12:00:00 UTC; path=/";
        document.location = "/springmvc/user/reg";
    }
</script>
<!--发送的是 get 请求-->
<button onclick="sendCookie()">向服务器端发送Cookie</button>
html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:thn="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
</head>
<body>
<!--注册页面-->
<form th:action="@{/user/reg}" 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="interest" value="Reading">
    敲代码<input type="checkbox" name="interest" value="Type the code">
    学习<input type="checkbox" name="interest" value="study">
    <br>
    简介
    <textarea cols="60" rows="10" name="intro"></textarea>
    <br>
    年龄:<input type="text" name="age"><br>
    <input type="submit" value="注册">
</form>

<script type="text/javascript">
    function sendCookie(){
        document.cookie = "id=123456789; expires=Thu, 18 Dec 2025 12:00:00 UTC; path=/";
        document.location = "/springmvc/user/reg";
    }
</script>
<!--发送的是 get 请求-->
<button onclick="sendCookie()">向服务器端发送Cookie</button>

</body>
</html>

后端 UserController代码:

java 复制代码
import com.rainbowsea.springmvc.pojo.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Arrays;

@Controller  // 交给 Spring IOC 容器管理
public class UserController {
    // 首页
    @RequestMapping("/")
    public String toRegisterPage() {
        return "register";
    }


    @GetMapping("/user/reg")
    public String register(User user,
                           @RequestHeader(value = "Referer", required = false, defaultValue = "") String referer,
                           @RequestHeader(value = "Host", required = false, defaultValue = "") String host,
                           //@CookieValue(value = "cookie的名字") String id
                           @CookieValue(value = "id", required = false, defaultValue = "") String id
    ) {

        System.out.println(user);
        System.out.println(referer); // Referer: http://localhost:8080/springmvc/
        System.out.println(host);  // Host: localhost:8080
        System.out.println("客户端提交过来的 cookie ,它的值是 " + id);
        return "ok";

    }
}

测试结果:

6. 补充:请求的中文乱码问题

6.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请求乱码问题:

html 复制代码
<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请求了。

java 复制代码
@GetMapping("/register")
public String register(User user){
    System.out.println(user);
    return "ok";
}

测试结果:


6.2 post 请求中文乱码

post请求是解决请求体的中文乱码问题。解决办法大家都知道:

java 复制代码
request.setCharacterEncoding("UTF-8");
post请求乱码如何解决?
request.setCharacterEncoding("UTF-8")
但是有一个前提
 request.setCharacterEncoding("UTF-8") ; 这一行代码必须在 request.getParameter("") 方法之前执行,才有效。
在Tomcat10当中,我们是不需要考虑post请求乱码问题,因为Tomcat10,已经自动帮助我们执行了,request.setCharacterEncoding("UTF-8")
在哪里可以看到呢?
> 在 CATALINA_HOME/cont/web.xml 文件中有这样的配置
> <request-character-encoding>UTF-8</request-character-encoding>
> <response-character-encoding>UTF-8</response-character-encoding>
> 这个配置信息表示,请求体采用UTF-8的方式,另外响应的时候也采用UTF-8的方式,所以POST请求无乱码,响应也没有乱码
> 注意了,这个Tomcat9以及之前的版本来说,没有以上的配置,Post请求乱码问题,响应的乱码问题需要自行解决
> 那么如果遇到Tomcat9版本,那么Post请求乱码应该怎么解决呢?对于SpringMVC来说,有什么好办法呢?
>  在request.getParamer()方法执行之前,执行 request.setCharacterEncoding("UTF-8");这样问题就解决了
>  

同样,对于高版本的Tomcat10 服务器来说,针对请求体中的字符编码也是配置好的,默认也是采用了UTF-8,中文乱码问题也解决了,在这个文件中配置的:apache-tomcat-10.1.19\conf\web.xml

配置内容如下:

通过以上配置可以看到,Tomcat10对请求和响应都设置了默认的字符编码方式为UTF-8
一定要注意:Tomcat9以及之前的版本,以上的配置是没有的。

我们来测试一下,针对Tomcat10来说,SpringMVC会不会有乱码问题:

html 复制代码
<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请求

java 复制代码
@PostMapping("/register")
public String register(User user, HttpServletRequest request) throws UnsupportedEncodingException {
    System.out.println(user);
    return "success";
}

测试结果:

通过测试可以看到在Tomcat10当中,默认SpringMVC,发送POST请求,是不会出现乱码问题的。

有可能很多同学使用的不是Tomcat10,如果不是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");无效我们来测试一下:

html 复制代码
<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请求

java 复制代码
@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在Servlet执行之前执行。

第二种方案:使用SpringMVC框架内置的字符编码过滤器即可,CharacterEncodingFilter.

这里是第二种方案:没错,我相信大家想到了:过滤器Filter。过滤器Filter可以在Servlet执行之前执行。有同学又说了:监听器不行吗?不行。因为我们需要对每一次请求解决乱码,而监听器只在服务器启动阶段执行一次。因此这里解决每一次请求的乱码问题,应该使用过滤器Filter。并且,告诉大家一个好消息,SpringMVC已经将这个字符编码的过滤器提前写好了,我们直接配置好即可:CharacterEncodingFilter,我们一起看一下它的源码:

最核心的方法是:

java 复制代码
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String encoding = this.getEncoding();
        if (encoding != null) {
            if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
                request.setCharacterEncoding(encoding);
            }

            if (this.isForceResponseEncoding()) {
                response.setCharacterEncoding(encoding);
            }
        }

        filterChain.doFilter(request, response);
    }

分析以上核心方法得知该过滤器对请求和响应都设置了字符编码方式。

  • isForceRequestEncoding 强行使用请求字符编码方式为true时,或者getCharacterEncoding 请求对象的字符编码方式为null时,设置请求的字符编码方式。

  • isForceResponseEncoding 强行使用响应字符编码方式为true时,设置响应的字符编码方式

根据以上代码,可以得出以下配置信息,在web.xml文件中对过滤器进行如下配置:

xml 复制代码
<!--    使用 Spring MVC 框架内置的字符编码过滤器-->
    <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>
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_5_0.xsd"
         version="5.0">



<!--    前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


<!--    使用 Spring MVC 框架内置的字符编码过滤器-->
    <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>
</web-app>

我们再来测试,重启Tomcat10,看看乱码是否能够解决?

注意:针对于我们当前的Tomcat10的配置(默认安装的Tomcat10是不需的,这里说的仅仅是我个人的)来说,它有默认的字符集ISO-8859-1,因此以下在web.xml文件中的配置是不能缺少的

xml 复制代码
<init-param>
    <param-name>forceRequestEncoding</param-name>
    <param-value>true</param-value>
</init-param>

如果缺少它,仍然是会存在乱码问题的。自行测试一下!!!!

6.3 中文乱码小结

java 复制代码
关于Javaweb项目中,get请求的乱码问题?
get请求,提交的数据是在浏览器的地址栏上回显,在请求行上提交数据,例如:/springmvc/login?username=张三&password=123
怎么解决get请求乱码问题?
 对url进行编码设置,在哪里可以设置URL的编码方式呢
> 在Tomcat 服务器的配置(CATALINA_HOME/conf/server.xml文件中)
> 对于Tomcat 10来说,get请求没有乱码,也就是说 Tomcat10已经自动对URI进行编码,并且默认的编码方式就是UTR-8
>           <Connector port=:"8080" protocol =HTTP/1.1
>                   connectionTimeout="20000"
>                   redirectPort = "8443"
>                   maxParameterCount="1000"
>                   URIEncoding="UTF-8"
>                   />
> 对于Tomcat10 和 Tomcat9 来说,get请求没有乱码,也就是说Tomcat10或者Tomcat9已经自动对URI进行编码,并且默认的编码方式就是UTF-8
> 但是对于Tomcat8来说,URIEncoding的默认值是ISO-8859-1编码方式,所以在Tomcat8中,get请求是存在中文乱码问题的,怎么解决,如上述所述
> 

关于JavaWeb项目中,Post请求的乱码问题?
post请求乱码如何解决?
request.setCharacterEncoding("UTF-8")
但是有一个前提
 request.setCharacterEncoding("UTF-8") ; 这一行代码必须在 request.getParameter("") 方法之前执行,才有效。
在Tomcat10当中,我们是不需要考虑post请求乱码问题,因为Tomcat10,已经自动帮助我们执行了,request.setCharacterEncoding("UTF-8")
在哪里可以看到呢?
> 在 CATALINA_HOME/cont/web.xml 文件中有这样的配置
> <request-character-encoding>UTF-8</request-character-encoding>
> <response-character-encoding>UTF-8</response-character-encoding>
> 这个配置信息表示,请求体采用UTF-8的方式,另外响应的时候也采用UTF-8的方式,所以POST请求无乱码,响应也没有乱码
> 注意了,这个Tomcat9以及之前的版本来说,没有以上的配置,Post请求乱码问题,响应的乱码问题需要自行解决
> 那么如果遇到Tomcat9版本,那么Post请求乱码应该怎么解决呢?对于SpringMVC来说,有什么好办法呢?
>  在request.getParamer()方法执行之前,执行 request.setCharacterEncoding("UTF-8");这样问题就解决了
>  
>第一种方案,自己编写一个过滤器,过滤器Filter在Servlet执行之前执行。
> 第二种方案:使用SpringMVC框架内置的字符编码过滤器即可,CharacterEncodingFilter.
```java
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String encoding = this.getEncoding();
        if (encoding != null) {
            if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {
                request.setCharacterEncoding(encoding);
            }

            if (this.isForceResponseEncoding()) {
                response.setCharacterEncoding(encoding);
            }
        }

        filterChain.doFilter(request, response);
    }
}
```

7. 总结:

  1. 获取请求数据的四种方式:

    1. 第一种:在Spring MVC 框架中使用原生的 Servlet API 进行获取

    2. 第二种方式:在 Spring MVC 框架中 使用 @RequestParam 注解标注,获取请求数据。注意其中的 required 属性和 defaultValue 属性的使用。value = name 是同一个属性

    3. 第三种方式:依靠控制器方法上的形参名来接收(简单的说就是可以省略 @RequestParam注解也能获取到值 )

    4. 第四种方式:使用 POJO类/JavaBean 接收请求参数 (这是最常用的)

  2. Spring MVC 中的 @RequestHeader注解 获取到"请求头"上的信息

  3. Spring MVC 中的 @CookieValue注解 获取到 Cookie 数据当中的值

  4. 补充:请求的中文乱码问题:

    1. 第一种方案,自己编写一个过滤器,过滤器Filter在Servlet执行之前执行。
    2. 第二种方案:使用SpringMVC框架内置的字符编码过滤器即可,CharacterEncodingFilter.
  5. Tomcat10以上的版本是没有中文乱码问题,不需要额外的配置,Tomcat 10以下的存在中文乱码问题,就需要配置了。

8. 最后:

"在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。"

相关推荐
xlsw_2 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹2 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫3 小时前
泛型(2)
java
超爱吃士力架3 小时前
邀请逻辑
java·linux·后端
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石3 小时前
12/21java基础
java
李小白663 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp4 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶4 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb