Spring MVC 的 @RequestMapping 注解的属性值详细讲解

1. Spring MVC 的 @RequestMapping 注解的属性值详细讲解

2. 准备工作

第一步导入:相关 jar 包信息:

在这里插入图片描述

<?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.rainbow</groupId>
    <artifactId>springmvc-003-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>
        <!-- Spring MVC依赖 -->
        <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>
            <!--            指定依赖的访问,provided 表示这个依赖由第三方容器来提供-->
            <!--            打war包的时候,这个依赖不会打入war包内,因为这个依赖由其它容器来提供的-->
            <!--            为什么这么做:是因为,我们的tomcat本身就带 servlet 的依赖,防止冲突,以及重复-->
            <!--            同时注意:我们这里用的是:jakarta.servlet 所以要用的 tomcat9以上才行-->
            <scope>provided</scope>
        </dependency>
        <!--Spring6和Thymeleaf整合依赖-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring6</artifactId>
            <version>3.1.2.RELEASE</version>
        </dependency>
    </dependencies>


</project>

第二步:创建相关文件和目录:

在这里插入图片描述

第三步: 编写 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_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>
</web-app>

第四步:编写 springmvc 的配置文件信息:视图解析器,组件扫描

在这里插入图片描述

<?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>

想要了解上述配置的,详细内容,大家可以移步至:✏️✏️✏️
初识 SpringMVC,运行配置第一个Spring MVC 程序-CSDN博客

3. @RequestMapping 注解的作用

@RequestMapping 注解是 Spring MVC 框架中的一个控制器映射注解。用于将请求映射到相应的处理方法上,具体来说,它可以将指定 URL 的请求绑定到一个特定的方法或类上,从而实现对请求的处理和响应。

3.1 @RequestMapping 注解可以出现的位置

在这里插入图片描述

//
// 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.aot.hint.annotation.Reflective;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
@Reflective({ControllerMappingReflectiveProcessor.class})
public @interface RequestMapping {
    String name() default "";

    @AliasFor("path")
    String[] value() default {};

    @AliasFor("value")
    String[] path() default {};

    RequestMethod[] method() default {};

    String[] params() default {};

    String[] headers() default {};

    String[] consumes() default {};

    String[] produces() default {};
}

通过RequestMapping的源码可以看到RequestMapping注解只能出现在类上或者方法上。

3.2 将 @RequestMapping 注解定义到 “ 类上” 与 “方法”上结合使用

我们先来看,在同一个web应用中,是否可以有两个完全一样的RequestMapping。测试一下:假设两个RequestMapping,其中一个是展示用户详细信息,另一个是展示商品详细信息。提供两个Controller,一个是UserController,另一个是ProductController。如下:

在这里插入图片描述

package com.rainbowsea.springmvc.controller;


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

@Controller  // 将该类交给 Spring IOC 容器管理
public class UserController {


    @RequestMapping(value = "/detail")  // 请求映射
    public String userDetail() {
        // 逻辑视图映射
        return "user_detail";
    }
}

package com.rainbowsea.springmvc.controller;


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

@Controller // 交给Spring IOC 容器管理
public class ProductController {


    @RequestMapping(value = "/detail")
    public String productDetail() {

        return "product_detail";
    }


}

编写对应的物理视图文件信息。

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>商品详情</title>
</head>
<body>
<h1>商品详情</h1>

</body>
</html>

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户详情</title>
</head>
<body>
<h1>用户详情</h1>

</body>
</html>

以上两个Controller的RequestMapping相同,都是 “/detail” ,我们来启动服务器看会不会出现问题:异常发生了,异常信息如下

在这里插入图片描述

16:28:13.822 [RMI TCP Connection(2)-127.0.0.1] DEBUG _org.springframework.web.servlet.HandlerMapping.Mappings -- 
	c.r.s.c.ProductController:
	{ [/detail]}: productDetail()
16:28:13.834 [RMI TCP Connection(2)-127.0.0.1] DEBUG _org.springframework.web.servlet.HandlerMapping.Mappings -- 
	c.r.s.c.UserController:
	{ [/detail]}: userDetail()
16:28:13.835 [RMI TCP Connection(2)-127.0.0.1] WARN org.springframework.web.context.support.XmlWebApplicationContext -- Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping': Ambiguous mapping. Cannot map 'userController' method 
com.rainbowsea.springmvc.controller.UserController#userDetail()
to { [/detail]}: There is already 'productController' bean method
com.rainbowsea.springmvc.controller.ProductController#productDetail() mapped.
16:28:13.837 [RMI TCP Connection(2)-127.0.0.1] ERROR org.springframework.web.servlet.DispatcherServlet -- Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping': Ambiguous mapping. Cannot map 'userController' method 
com.rainbowsea.springmvc.controller.UserController#userDetail()
to { [/detail]}: There is already 'productController' bean method
com.rainbowsea.springmvc.controller.ProductController#productDetail() mapped.

以上异常信息大致的意思是:不明确的映射。无法映射 UserController 中的toDetail()方法,因为已经在 ProductController 中映射过了!!!!
通过测试得知,**在同一个 webapp 中,RequestMapping 必须具有唯一性。**怎么解决以上问题?

两种解决方案:

  • 第一种方案:将方法上RequestMapping的映射路径修改的不一样。

  • 第二种方案:在类上添加RequestMapping的映射路径,以类上的RequestMapping作为命名空间,来加以区分两个不同的映射。

**第一种方案:将方法上RequestMapping的映射路径修改的不一样。 **

在这里插入图片描述

package com.rainbowsea.springmvc.controller;


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

@Controller  // 将该类交给 Spring IOC 容器管理
public class UserController {


    @RequestMapping(value = "/user/detail") // 请求映射
    public String userDetail() {
        // 逻辑视图映射
        return "user_detail";
    }
}

package com.rainbowsea.springmvc.controller;


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

@Controller // 交给Spring IOC 容器管理
public class ProductController {


    @RequestMapping(value = "/product/detail")
    public String productDetail() {

        return "product_detail";
    }


}

运行测试:

在这里插入图片描述

第二种方案:在类上添加 RequestMapping的映射路径,以类上的 RequestMapping 作为命名空间,来加以区分两个不同的映射。

在类上和方法上都使用 RequestMapping 注解来进行路径的映射。假设在类上映射的路径是"/a",在方法上映射的路径是"/b",那么整体表示映射的路径就是:“/a/b”
在第一种方案中,假设UserController类中有很多方法,每个方法的 RequestMapping注解中都需要以"/user"开始,显然比较啰嗦,干脆将"/user"提升到类级别上,例如下:

package com.rainbowsea.springmvc.controller;


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

@Controller  // 将该类交给 Spring IOC 容器管理
@RequestMapping(value = "/user")
public class UserController {


    @RequestMapping(value = "/detail") // 请求映射
    public String userDetail() {
        // 逻辑视图映射
        return "user_detail";
    }
}

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.springmvc.controller;


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

@Controller  // 将该类交给 Spring IOC 容器管理
@RequestMapping(value = "/user")
public class UserController {


    @RequestMapping(value = "/detail") // 请求映射
    public String userDetail() {
        // 逻辑视图映射
        return "user_detail";
    }
}

在这里插入图片描述

package com.rainbowsea.springmvc.controller;


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

@Controller // 交给Spring IOC 容器管理

@RequestMapping(value = "/product")
public class ProductController {


    @RequestMapping(value = "/detail")
    public String productDetail() {

        return "product_detail";
    }


}

运行测试:

在这里插入图片描述

4. @RequestMapping注解的 value 属性

4.1 value属性的使用

value 属性是该注解最核心的属性,value 属性填写的请求路径,也就是说通过该请求路径与对应的控制器的方法绑定在一起。

从源代码上看,我们可以看到 value 属性是一个字符串数组:既既然是数组,就表示可以提供多个路径,也就是说,在 Spring MVC 中,多个不同的请求路径可以映射同一个控制器的同一个方法。

在这里插入图片描述

编写新的控制器:

在这里插入图片描述

package com.rainbowsea.springmvc.controller;



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

@Controller // 交给 Spring IOC 容器管理
public class IndexController {

    @RequestMapping(value = "/")  // 首页
    public String index() {
        return "index";
    }


    // 对于注解来说,如果是一个数组,数组中只有一个元素,大括号是可以省略的
    //@RequestMapping({"testvalue1","testvalue1"})
    //@RequestMapping(value = {"testvalue1","testvalue2"})
    // path 和 value 是同一个值
    @RequestMapping(path = {"testvalue1","testvalue2"})
    public String testRequestMappingValue() {
        // 处理业务逻辑
        // 返回逻辑视图
        return "test_value";
    }

}

提供视图页面:

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>value</title>
</head>
<body>

<h1> RequestMapping 注解的 Value 测试</h1>

</body>
</html>

在index.html文件中添加两个超链接:对应上配置的两个 映射文件

在这里插入图片描述

在这里插入图片描述

<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

<h1>首页</h1>

<!--测试 RequestMapping 注解的 value 值-->
<h2>测试 RequestMapping 注解的 value 值-</h2>
<a th:href="@{/testvalue1}">RequestMapping 注解的 Value 属性1</a>
<a th:href="@{/testvalue2}">RequestMapping 注解的 Value 属性2</a>
<br>


</body>
</html>

运行测试:

启动服务器,测试,点击以下的两个超链接,发送请求,都可以正常访问到同一个控制器上的同一个方法:

在这里插入图片描述

特殊说明:

value属性本身是一个 String[] 字符串数组,说明多个请求路径可以映射同一个处理器方法。
如果注解的属性是数组,并且在使用注解的时候,该数组中只有一个元素,大括号可以省略
如果使用某个注解的时候,如果只使用了一个 value属性,那么 value 也是可以省略的
value 属性的别名是 path
path 属性的别名是 value
path 和 value 是同一个东东

在这里插入图片描述

4.2 Ant 风格 的value

value 是可以用来匹配路径的,路径支持模糊匹配 ,我们把这种模糊匹配称之为 Ant 风格 ,关于路径中的通配符包括:

?表示任意一个字符。(除 / ? 之外的其它字符,注意,一定是一个字符,同时也不可以空着)
“*” 表示0到 N 个任意字符,(除 / ? 之外的其它字符)
“**” 表示 0 到 N 个任意字符,并且路径中可以出现 /,但是 ** 在使用的时候需要注意,**左边只能是 /
同时注意:
如果使用Spring5以及之后的版本,这样写是没问题的,@requestMapping(value = "/**/testAntValues")
如果使用Spring6以及之后的版本,这样写是报错的。,@requestMapping(value = "/**/testAntValues")
在Spring6当中 ,** 通配符只能作为路径的末尾出现
@RequestMapping(value = "/testAntValue/**") Spring65都可以

测试一下这些通配符,在 RequestMappingTestController 中添加以下方法:

在这里插入图片描述

package com.rainbowsea.springmvc.controller;



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

@Controller // 交给 Spring IOC 容器管理
public class IndexController {

    @RequestMapping(value = "/")  // 首页
    public String index() {
        return "index";
    }



    @RequestMapping(value = "/x?z/testAntValue")
    public String testRequestMappingAntValue() {
        return "ok";
    }



}

视图对应物理视图的编写:

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OK</title>
</head>
<body>

<h1>测试 RequestMapping 注解的 value 属性支持模糊匹配</h1>

</body>
</html>s

通过修改浏览器地址栏上的路径,可以反复测试通配符 ? 的语法:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

将 ? 通配符修改为 * 通配符:

在这里插入图片描述

//@RequestMapping(value = "/x?z/testAntValue")
    @RequestMapping(value = "/x*z/testAntValue")
    public String testRequestMappingAntValue() {
        return "ok";
    }

在这里插入图片描述

在这里插入图片描述

* 通配符修改为 ** 通配符:

在这里插入图片描述

    //@RequestMapping(value = "/x?z/testAntValue")
    //@RequestMapping(value = "/x*z/testAntValue")
    @RequestMapping(value = "/x**z/testAntValue")
    public String testRequestMappingAntValue() {
        return "ok";
    }


打开浏览器直接在地址栏上输入路径进行测试:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意:/xz/ 实际上并没有使用通配符 ,本质上还是使用的 * ,因为通配符 ** 在使用的时候,左右两边都不能有任何字符,必须是 /** 在使用的时候需要注意,** 左边只能是 /

    //@RequestMapping(value = "/x?z/testAntValue")
    //@RequestMapping(value = "/x*z/testAntValue")
    @RequestMapping(value = "/**/testAntValue")
    public String testRequestMappingAntValue() {
        return "ok";
    }

在这里插入图片描述

以上写法在Spring5的时候是支持的,但是在Spring6中进行了严格的规定,** 通配符只能出现在路径的末尾,例如:

在Spring6当中 ,** 通配符只能作为路径的末尾出现

@RequestMapping(value = "/testAntValue/**") Spring65都可以
@RequestMapping("/testValueAnt/**")
public String testValueAnt(){
    return "testValueAnt";
}

在这里插入图片描述

4.3 value 中 的 占位符 @PathVariable 注解获取值(重点)

到目前为止,我们的请求路径是这样的格式:

/springmvc/login?username = admin&password=123  

uri?name1=value1&name2=value2&name3=value3

其实除了上面的传统的URL。还有另外一种格式的请求路径,格式为:我们将下面这种请求路径叫做:RESTful 风格的请求路径。

RESTful 风格的请求路径在现代的开发中使用的较多。

/springmvc/login/admin/123
uri/value1/value2/value3
普通的请求路径:http://localhost:8080/springmvc/login?username=admin&password=123&age=20
RESTful风格的请求路径:http://localhost:8080/springmvc/login/admin/123/20

如果使用RESTful风格的请求路径,在控制器中应该如何获取请求中的数据呢?

可以在value属性中使用占位符,例如:/login/{id}/{username}/{password}

同时通过 @PathVariable()参数 上的注解,获取对应占位符的值,注意,这两者之间的名称要保持一致,才能成功获取到对应的值。

@RequestMapping(value = "/login/{a}/{b}")
    public String testRESTFulURL(@PathVariable("a") String username, @PathVariable("b") String password) {
        System.out.println("用户名: " + username + " 密码: " + password);
        return "ok";
    }

在 IndexController 类中添加一个方法:

在这里插入图片描述

package com.rainbowsea.springmvc.controller;



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

@Controller // 交给 Spring IOC 容器管理
public class IndexController {

    @RequestMapping(value = "/")  // 首页
    public String index() {
        return "index";
    }

    @RequestMapping(value = "/login/{username}/{password}")
    public String testRESTFulURL(@PathVariable("username") String username, @PathVariable("password") String password) {
        System.out.println("用户名: " + username + " 密码: " + password);
        return "ok";
    }

}

提供视图页面:

这里我们可以不用表单的方式提交数据,而是用 thymeleaf 语法的方式,进行一个超链接上的提交数据给后端。

在这里插入图片描述

<a th:href="@{login/admin/123}">测试RequestMapping 注解的 params 属性</a><br>

启动服务器测试:

在这里插入图片描述

在这里插入图片描述

5. @ RequestMapping注解的 method 属性

在 Servlet 当中,

  • 如果后端要求前端必须发送一个 Post 请求,后端可以通过重写 doPost( ) 方法来实现。
  • 后端要求前端必须发送一个 Get请求,后端可以通过重写 doGet( ) 方法来实现。
  • 当重写的方法是 doPost( ) 时,前端必须发送Post 请求。
  • 当重写的方法是 doGet( ) 时,前端就必须发送 get 请求。
  • 如果前端发送请求的方式和后端的处理方式不一致时,会出现 405 错误。

HTTP 状态码 405 ,这种机制的作用时:限制客户端的请求方式,以保证服务器中数据的安全。

假设后端程序要处理的请求时一个登录请求,为了保证登录时的用户名和密码不被显示到浏览器的地址栏上,后端程序有义务要求前端必须发送一个 Post 请求,如果前端发送 Get 请求,则应该拒绝。

那么在 Spring MVC 框架中应该如何实现这种机制呢?

可以使用 Spring MVC @RequestMapping 注解当中的 methode 属性来实现。

在这里插入图片描述

从上述源代码上我们可以清晰的看到,method 属性也是一个数组。

数组中的每个元素时 RequestMehtod,而这个 RequestMehtod 时一个枚举类型的数据。

在这里插入图片描述

因此如果要求前端发送POST请求,该注解应该这样用:

在这里插入图片描述

在这里插入图片描述

    // 当前端发送的请求路径是:/login ,并且发送的请求方式是以POST方式请求的,则可以正常映射
    // 当前端发送的请求路径不是:/login ,就算请求方式是以POST方式请求的,也不会映射到这个方法上
    // 当前端发送的请求路径是:/login ,请求方式是以get方式请求的,也不会映射到这个方法上
    @RequestMapping(value = "/login",method = {RequestMethod.POST})
    public String userLogin() {
        System.out.println("处理登陆的业务...");
        return "ok";
    }
    // Method 'GET' is not supported.

接下来,我们来测试一下:
在 IndexController 类中添加以下方法:

在这里插入图片描述

package com.rainbowsea.springmvc.controller;



import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller // 交给 Spring IOC 容器管理
public class IndexController {


    @RequestMapping(value = "/")  // 首页
    public String index() {
        return "index";
    }

    // 当前端发送的请求路径是:/user/login ,并且发送的请求方式是以POST方式请求的,则可以正常映射
    // 当前端发送的请求路径不是:/user/login ,就算请求方式是以POST方式请求的,也不会映射到这个方法上
    // 当前端发送的请求路径是:/user/login ,请求方式是以get方式请求的,也不会映射到这个方法上
    @RequestMapping(value = "/login",method = {RequestMethod.POST})
    public String userLogin() {
        System.out.println("处理登陆的业务...");
        return "ok";
    }
    // Method 'GET' is not supported.
}

提供视图页面:

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>OK</title>
</head>
<body>

<h1>OK</h1>

</body>
</html>

在 index.html 页面中提供一个登录的form表单,后端要求发送post请求,则form表单的method属性应设置为 post

在这里插入图片描述

<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

<h1>首页</h1>



<!--测试 RequestMapping 注解的 value 值-->
<h2>测试 RequestMapping 注解的 value 值-</h2>
<a th:href="@{/testvalue1}">RequestMapping 注解的 Value 属性1</a>
<a th:href="@{/testvalue2}">RequestMapping 注解的 Value 属性2</a>

<br>

<form th:action="@{/login}" method="post">
    用户名: <input type="text" name="username" /><br>
    密码:<input type="password" name="password" \><br>
    <input type="submit" value="登录">
</form>
<br>

</body>
</html>

启动服务器,测试:

在这里插入图片描述

通过测试,前端发送的请求方式 post,后端处理请求的方式也是post,就不会有问题。
当然,如果后端要求前端必须发送 post请求,而前端发送了get请求,则会出现405错误,

将index.html中form表单提交方式修改为 get

在这里插入图片描述

再次测试:

在这里插入图片描述

因此,可以看出,对于RequestMapping注解来说,多一个属性,就相当于多了一个映射的条件,如果value和method属性都有,则表示只有前端发送的请求路径 + 请求方式都满足时才能与控制器上的方法建立映射关系,只要有一个不满足,则无法建立映射关系。例如:@RequestMapping(value=“/login”, method = RequestMethod.POST) 表示当前端发送的请求路径是 /login,并且发送请求的方式是POST的时候才会建立映射关系。如果前端发送的是get请求,或者前端发送的请求路径不是 /login,则都是无法建立映射的。

6. @ RequestMapping注解的 衍生Mapping 属性

对于以上的程序来说,SpringMVC提供了另一个注解,使用这个注解更加的方便,它就是:@PostMapping,使用该注解时,不需要指定 method 属性,因为它默认采用的就是 POST处理方式:修改IndexController代码如下

在这里插入图片描述

    // 当前端发送的请求路径是:/login ,并且发送的请求方式是以POST方式请求的,则可以正常映射
    // 当前端发送的请求路径不是:/login ,就算请求方式是以POST方式请求的,也不会映射到这个方法上
    // 当前端发送的请求路径是:/login ,请求方式是以get方式请求的,也不会映射到这个方法上
    //@RequestMapping(value = "/login",method = {RequestMethod.POST})
    @PostMapping
    public String userLogin() {
        System.out.println("处理登陆的业务...");
        return "ok";
    }
    // Method 'GET' is not supported.

在这里插入图片描述

在SpringMVC中不仅提供了 PostMaping注解,像这样的注解还有四个,包括:

  • GetMapping:要求前端必须发送get请求

  • PutMapping:要求前端必须发送put请求

  • DeleteMapping:要求前端必须发送delete请求

  • PatchMapping:要求前端必须发送patch请求

7. 补充:web 的请求方式

端向服务器发送请求的方式包括哪些?共9种,前5种常用,后面作为了解:

  • GET:获取资源,只允许读取数据,不影响数据的状态和功能。使用 URL 中传递参数或者在 HTTP 请求的头部使用参数,服务器返回请求的资源。

  • POST:向服务器提交资源,可能还会改变数据的状态和功能。通过表单等方式提交请求体,服务器接收请求体后,进行数据处理。

  • PUT:更新资源,用于更新指定的资源上所有可编辑内容。通过请求体发送需要被更新的全部内容,服务器接收数据后,将被更新的资源进行替换或修改。

  • DELETE:删除资源,用于删除指定的资源。将要被删除的资源标识符放在 URL 中或请求体中。

  • HEAD:请求服务器返回资源的头部,与 GET 命令类似,但是所有返回的信息都是头部信息,不能包含数据体。主要用于资源检测和缓存控制。

  • PATCH:部分更改请求。当被请求的资源是可被更改的资源时,请求服务器对该资源进行部分更新,即每次更新一部分。

  • OPTIONS:请求获得服务器支持的请求方法类型,以及支持的请求头标志。“OPTIONS *”则返回支持全部方法类型的服务器标志。

  • TRACE:服务器响应输出客户端的 HTTP 请求,主要用于调试和测试。

  • CONNECT:建立网络连接,通常用于加密 SSL/TLS 连接。

注意:

  1. 使用超链接以及原生的form表单只能提交get和post请求,put、delete、head请求可以使用发送ajax请求的方式来实现。
  2. 使用超链接发送的是get请求
  3. 使用form表单,如果没有设置method,发送get请求
  4. 使用form表单,设置method=“get”,发送get请求
  5. 使用form表单,设置method=“post”,发送post请求
  6. 使用form表单,设置method=“put/delete/head”,发送get请求。(针对这种情况,可以测试一下)

将 index.html 中登录表单的提交方式 method 设置为 put:

在这里插入图片描述

修改IndexController类的代码为:RequestMethod.PUT

在这里插入图片描述

运行测试:

在这里插入图片描述

通过测试得知,即使form中method设置为put方式,但仍然采用get方式发送请求。

再次修改 修改IndexController类代码为:RequestMethod.GET

@RequestMapping(value="/login", method = RequestMethod.GET)
//@PostMapping("/login")
public String testMethod(){
    return "testMethod";
}

再次测试:

在这里插入图片描述

7.1 GET和POST的区别

在之前发布的JavaWEB视频中对HTTP请求协议的GET和POST进行了详细讲解,这里就不再赘述,大致回顾一下。
HTTP请求协议之GET请求:

GET /springmvc/login?username=lucy&userpwd=1111 HTTP/1.1                           请求行
Host: localhost:8080                                                                    请求头
Connection: keep-alive
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/springmvc/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
                                                                                        空白行
                                                                                        请求体

HTTP请求协议之POST请求:

POST /springmvc/login HTTP/1.1                                                  请求行
Host: localhost:8080                                                                  请求头
Connection: keep-alive
Content-Length: 25
Cache-Control: max-age=0
sec-ch-ua: "Google Chrome";v="95", "Chromium";v="95", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/springmvc/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
                                                                                      空白行
username=lisi&userpwd=123                                                             请求体

Get 和 Post 的区别是什么:

  1. get请求发送数据的时候,数据会挂在URI的后面,并且在URI后面添加一个“?”,"?"后面是数据。这样会导致发送的数据回显在浏览器的地址栏上。
  2. http://localhost:8080/springmvc/login?username=zhangsan&userpwd=1111
  3. post请求发送数据的时候,在请求体当中发送。不会回显到浏览器的地址栏上。也就是说post发送的数据,在浏览器地址栏上看不到。
  4. get请求只能发送普通的字符串。并且发送的字符串长度有限制,不同的浏览器限制不同。这个没有明确的规范。get请求无法发送大数据量。
  5. post请求可以发送任何类型的数据,包括普通字符串,流媒体等信息:视频、声音、图片。post请求可以发送大数据量,理论上没有长度限制。
  6. get请求在W3C中是这样说的:get请求比较适合从服务器端获取数据。
  7. post请求在W3C中是这样说的:post请求比较适合向服务器端传送数据。
  8. get请求是安全的。因为在正确使用get请求的前提下,get请求只是为了从服务器上获取数据,不会对服务器数据进行修改。
  9. post请求是危险的。因为post请求是修改服务器端的资源。
  10. get请求支持缓存。 也就是说当第二次发送get请求时,会走浏览器上次的缓存结果,不再真正的请求服务器。(有时需要避免,怎么避免:在get请求路径后添加时间戳)
  11. post请求不支持缓存。每一次发送post请求都会真正的走服务器

7.2 GET 和 POST 怎么选择

  1. 如果你是想从服务器上获取资源,建议使用GET请求,如果你这个请求是为了向服务器提交数据,建议使用POST请求。
  2. 大部分的form表单提交,都是post方式,因为form表单中要填写大量的数据,这些数据是收集用户的信息,一般是需要传给服务器,服务器将这些数据保存/修改等。
  3. 如果表单中有敏感信息,建议使用post请求,因为get请求会回显敏感信息到浏览器地址栏上。(例如:密码信息)
  4. 做文件上传,一定是post请求。要传的数据不是普通文本。
  5. 其他情况大部分都是使用get请求。

关于 GET 和 POST 请求的更多内容,大家可以移步至:✏️✏️✏️ Sevlet规范:HttpServlet类 和 HttpServletRequest接口 源码解析-CSDN博客

8. @RequestMapping注解的 params 属性

params 属性用来设置通过请求参数来映射请求。

对于 RequestMapping 注解来说:

  • value 属性是一个数组,只要满足数组中的任意一个路径,就能映射成功
  • method 属性也是一个数组,只要满足数组中任意一个请求方式,就能映射成功。
  • params 属性也是一个数组,不过要求请求参数必须和 params 数组中要求的所有参数完全一致后,才能映射成功。

在这里插入图片描述

8.1 params 属性的 4 种用法

@RequestMapping(value="/login", params={"username", "password"}) 表示:请求参数中必须包含 username 和 password,才能与当前标注的方法进行映射。
@RequestMapping(value="/login", params={"!username", "password"}) 表示:请求参数中不能包含username参数,但必须包含password参数,才能与当前标注的方法进行映射。
@RequestMapping(value="/login", params={"username=admin", "password"}) 表示:请求参数中必须包含username参数,并且参数的值必须是admin,另外也必须包含password参数,才能与当前标注的方法进行映射。
@RequestMapping(value="/login", params={"username!=admin", "password"}) 表示:请求参数中必须包含username参数,但参数的值不能是admin,另外也必须包含password参数,才能与当前标注的方法进行映射。

注意:如果前端提交的参数,和上面我们后端 params 属性的要求的请求参数不一致(不满足条件),则出现 400 错误!!!

HTTP状态码400的原因:请求参数格式不正确而导致的。

 // 当请求路径是 /testParams,并且提交的参数包括 username 和 password 时,才能映射成功
    @RequestMapping(value = "/testParams", params = {"username","password"})
    //@RequestMapping(value = "/testParams", params = {"username=zhangsan","password"})
    //@RequestMapping(value = "/testParams", params = {"username!=zhangsan","password"})
    //@RequestMapping(value = "/testParams", params = {"!username","password"})

    public String testParam() {
        return "ok";
        // Invalid request parameters.
    }

8.2 测试 params 属性

在 IndexController类中添加以下方法:

在这里插入图片描述

    // 当RequestMapping 注解当中条件了 params ,则表示添加了新的约束条件
    // 当请求路径是 /testParams,并且提交的参数包括 username 和 password 时,才能映射成功
    @RequestMapping(value = "/testParams", params = {"username","password"})
    public String testParam() {
        return "testParams";
        // Invalid request parameters.
    }

提供视图页面:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>testParams</title>
</head>
<body>
<h1>测试RequestMapping注解的Params属性</h1>
</body>
</html>

在index.html文件中添加超链接:

<!--测试RequestMapping的params属性-->
<a th:href="@{/testParams(username='admin',password='123')}">测试params属性</a>

当然,你也可以这样写(使用 thymeleaf 模板视图):这样写IDEA会报错,但不影响使用。

<a th:href="@{/testParams?username=admin&password=123}">测试params属性</a><br>

在这里插入图片描述

启动服务器,测试:

在这里插入图片描述


在这里插入图片描述

假如发送请求时,没有传递username参数会怎样?

<a th:href="@{/testParams(password='123')}">测试params属性</a><br>

启动服务器,测试:

在这里插入图片描述

在这里插入图片描述

提示无效的请求参数,服务器无法或不会处理当前请求。
params 属性剩下的三种情况,自行测试 !!!

9. @RequestMapping注解的 headers 属性

在这里插入图片描述

headers 和 params 原理相同,用法也相同。
当前端提交的请求头信息和后端要求的请求头信息一致时,才能映射成功。
请求头信息怎么查看?在 chrome浏览器 中,F12 打开控制台,找到 Network,可以查看具体的请求协议和响应协议。在请求协议中可以看到请求头信息,例如:

在这里插入图片描述

请求头信息和请求参数信息一样,都是键值对形式,例如上图中:

Referer: http://localhost:8080/springmvc/     键是Referer,值是http://localhost:8080/springmvc/
Host: localhost:8080     键是Host,值是localhost:8080

9.1 headers 属性的 4 种用法

@RequestMapping(value="/login", headers={"Referer", "Host"}) 表示:请求头信息中必须包含RefererHost,才能与当前标注的方法进行映射。
@RequestMapping(value="/login", headers={"Referer", "!Host"}) 表示:请求头信息中必须包含Referer,但不包含Host,才能与当前标注的方法进行映射。
@RequestMapping(value="/login", headers={"Referer=http://localhost:8080/springmvc/", "Host"}) 表示:请求头信息中必须包含RefererHost,并且Referer的值必须是http://localhost:8080/springmvc/,才能与当前标注的方法进行映射。
@RequestMapping(value="/login", headers={"Referer!=http://localhost:8080/springmvc/", "Host"}) 表示:请求头信息中必须包含RefererHost,并且Referer的值不是http://localhost:8080/springmvc/,才能与当前标注的方法进行映射。

注意, headers 同样的,如果前端提交的请求头信息,和后端 header 属性要求的请求头信息不一致(条件不满足),则出现 404 错误!!!

9.2 测试 headers 属性

在 IndexController类中添加以下方法:

在这里插入图片描述

@RequestMapping(value="/testHeaders", headers = {"Referer=http://localhost:8080/springmvc/"})
public String testHeaders(){
    return "testHeaders";
}

提供视图页面:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>test Headers</title>
</head>
<body>
<h1>测试RequestMapping注解的headers属性</h1>
</body>
</html>

在 index.html 页面中添加超链接:

<!--测试RequestMapping的headers属性-->
<a th:href="@{/testHeaders}">测试headers属性</a><br>

启动服务器,测试结果:

在这里插入图片描述


将后端控制器中的 headers 属性值进行修改:

在这里插入图片描述

@RequestMapping(value="/testHeaders", headers = {"Referer=http://localhost:8888/springmvc/"})
public String testHeaders(){
    return "testHeaders";
}

再次测试:

在这里插入图片描述

10. 总结:

  1. 通过RequestMapping的源码可以看到RequestMapping注解只能出现在"类上"或者"方法上"。

  2. 在同一个 webapp 中,RequestMapping 必须具有唯一性。

  3. 从源代码上看,我们可以看到 value 属性是一个字符串数组:既既然是数组,就表示可以提供多个路径,也就是说,在 Spring MVC 中,多个不同的请求路径可以映射同一个控制器的同一个方法。

  4. 别名机制:path 是 value 属性的别名,而 path 也是 value 属性的别名。path = value 是同一个东东

  5. 可以在value属性中使用占位符,例如:/login/{id}/{username}/{password}

    同时通过 @PathVariable()参数 上的注解,获取对应占位符的值,注意,这两者之间的名称要保持一致,才能成功获取到对应的值。

    @RequestMapping(value = "/login/{a}/{b}")
        public String testRESTFulURL(@PathVariable("a") String username, @PathVariable("b") String password) {
            System.out.println("用户名: " + username + " 密码: " + password);
            return "ok";
        }
    
  6. 当 @RequestMapping 注解当中条件了 params ,则表示添加了新的约束条件。

  7. headers 和 params 原理相同,用法也相同。当前端提交的请求头信息和后端要求的请求头信息一致时,才能映射成功。

  8. @RequestMapping 注解当中添加的属性越多,约束条件也就越多。

11. 最后:

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

在这里插入图片描述

  • 36
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值