@RequestMapping注解的功能:
从注解名称上我们可以看出,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来
,建立映射关系,SpringMVC接收到指定的请求,就会来找到在映射关系中对应的控制方法来处理这个请求。
@RequestMapping注解的位置:
@RequestMapping标识一个类
:设置映射请求的请求路径的初始信息
@RequestMapping标识一个方法
:设置映射请求,请求路径的具体信息
如下所示:
当前我们Protal方法的访问路径是hello
package AnnotationController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class ProtalController { @RequestMapping("/hello") public String Protal(){ return "index"; } }
当前的控制层,我们只给控制层中的Protal方法加上了@RequestMapping注解,而并没有给该控制层的类上加,因此我们可以直接通过Protal方法上的请求路径访问。
假设我们此时也给类加上@RequestMapping注解,访问地址不变化,如下所示:
@RequestMapping("test") public class ProtalController {
访问结果如下所示,我们会发现当前资源无法被找到
要想资源 被正确的访问,我们需要进行如下操作:
在路径的具体信息之前添加路径的初始信息,因为当在类上添加了请求路径的初始信息之后,我们想访问该类中的某个方法,就必须先正确的访问到该类。
而这样做的目的在于,在实际开发中,我们往往会给请求方法设置较为简单的路径请求具体信息,例如查询学生的方法,我们会将其请求路径具体的信息设置select,删除学生的方法,我们会将其我们会将其请求路径具体的信息设置delete,而需求往往不是适合于一类人而已,对于除学生以外的其他用户,都会有增删的操作,这样就导致了,系统也不知道该访问哪一个,因此,我们需要在类上添加请求路径的初始化信息。
@RequestMapping注解value属性值:
我们通过查看@RequestMapping注解:
它的匹配方式有很多种,通过请求路径,通过请求方法…,而比较常用的就是通过path和method
// // 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.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping 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注解value属性:
通过查看源码可知,value的类型为String[],如下所示:
我们将@RequestMapping的值设置为多个
package AnnotationController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class ProtalController { //当匹配方式中只有value时,value可以省略不写 @RequestMapping({"/hello","/abc"}) public String Protal(){ return "index"; } }
那么我们是否可以根据数组中任意的一个值来访问到正确的资源呢?
我们将项目重新部署:
由此可知,@RequestMapping注解value属性的作用是
:通过请求的请求路径匹配请求
,value属性是数组类型
,即当前浏览器所发送请求的请求路径匹配value属性中的任何一个值,则当前请求就会被注解所标识的方法进行处理
@RequestMapping注解method属性:
通过查看源码如下所示,我们知道method属性是ReuestMethod类型的:
RequestMethod[] method() default {};
继续跟进查看ReuestMethod的源码,如下所示:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.web.bind.annotation; public enum RequestMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; private RequestMethod() { } }
我们发现它是一个枚举类型,列举了所有的请求方式,其中get和post是我们已经学过的,那么接下来我们就以post和get请求为例演示起作用。
创建实现页面跳转的模块
首页请求控制器:
package AnnotationController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class ProtalController { @RequestMapping("/") public String Protal(){ return "index"; } }
跳转页面请求控制器
package AnnotationController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class JumpPageController { @RequestMapping(value = {"/hello","/abc"},method= RequestMethod.GET) public String Page(){ return "success"; } }
index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>成功进入首页!</h1> <a th:href="@{/hello}">测试@RequestMapping注解所标识的位置</a><br> <a th:href="@{/abc}">测试@RequestMapping注解的value属性</a><br> <form th:action="@{/hello}"> <input type="submit" value="测试@RequestMapping注解的method属性"> </form> </body> </html>
success.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>成功</title> </head> <body> <h1>成功进入!</h1> </body> </html>
此时运行服务器,如下所示:
点击其中的任何一个连接,都会出现如下字样:
通过输出结果,我们不难发现将@RequestMapping注解中的method属性值设置为
RequestMethod.GET,也就是匹配get请求的方式是没有任何问题的,那么如果我们将其匹配的是post还会如此吗?
修改@RequestMapping注解中的method属性值为RequestMethod.POST,再次运行:
出现下述异常:
tomcat告诉我们是因为方法不被支持
那为什么当我们将将@RequestMapping注解中的method属性值设置为RequestMethod.GET就能正确的处理请求?原因是:无论是超链接还是表单,默认的提交方式都是get请求,因此当我们没有设置其值的情况情况下,默认的就是get请求方式
我们可以将表单的提交方式设置为POST,这样就可以处理POST请求了:
<form th:action="@{/hello}" method="post"> <input type="submit" value="测试@RequestMapping注解的method属性"> </form>
如下所示,通过method属性,将其表单提交的方式设置为post
此时点击该按钮就可以成功的进入success.html页面啦
但是需要注意的是,如果我们把当前可以访问成功的地址复制,直接在浏览器中访问,如下所示:
还是出现了405错误,原因是我们将地址复制直接通过地址栏访问,实际上是get请求,但是我们又在注解中将其请求方法设置为post请求,由于未能匹配,所以出现了405错误
通过查看它的源码,我们得知其method是一个数组类型的,那么我们就可以利用这一特性,让它既能匹配get请求,又能匹配post请求,设置如下所示:
@RequestMapping(value = {"/hello","/abc"},method= {RequestMethod.POST,RequestMethod.GET})
此时我们刷新刚刚复制地址,而导致报错的页面,就能成功访问啦,只要是get、post中的任意一个都能被成功访问
@RequestMapping注解的method属性的作用是通过请求的请求方式匹配请求,它RequestMethod类型的数组,即当前浏览器所发送请求的请求方式匹配method属性中的任何一个请求,如果匹配成功,那么当前请求就会被注解所标识的方法进行处理,若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但是请求方式不匹配,页面会报错,如下所示:
在@ResquestMapping的基础上,结合请求方式的一些派生注解:
@GetMapping,@PostMapping,@DeleteMapping,@PutMapping,关于这些注解在后续的学习中会使用。
@RequestMapping注解params属性:
String[] params() default {};
@RequestMapping注解params属性的作用是通过请求的请求参数匹配请求,即浏览器发送的请求的请求参数必须满足params属性的设置
,params可以使用四种表达式:
“param”:表示当前所匹配请求的请求参数中必须携带param参数
测试如下所示:
在注解中,我们添加param属性,并给定一个值为username
package AnnotationController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class JumpPageController { @RequestMapping(value = {"/hello","/abc"}, method= {RequestMethod.POST,RequestMethod.GET} ,params = {"username"}) public String Page(){ return "success"; } }
再重新进行项目的部署,点击超链接或者表单中的按钮,出现以下错误:
原因是,我们在注解中设置了其参数的值,而在地址栏中并没有该参数的值,所以会报错。
解决方法如下所示:
<!--以下两种方式毫无区别,唯一的区别是第一种写法在源文件中会报错,但是该报错不影响代码的编译和执行 --> <a th:href="@{/hello?username=admin}" >测试@RequestMapping注解的param属性</a><br> <a th:href="@{/hello(username='admin')}" >测试@RequestMapping注解的param属性</a>
通过这种方式,浏览器会在地址栏自动为我们添加其值,如下所示:
此时页面也能被成功的访问到!
但需要注意的是,param属性和method属性以及value属性可不一样哦,当我们将method属性和value属性中设置有多个值时,只要能匹配到其中的任意一个即可,但是对于param属性来说,如果设置的值有多个必须同时匹配,测试如下:
给param属性值增加password,其他地方不做任何的变动
@RequestMapping(value = {"/hello","/abc"}, method= {RequestMethod.POST,RequestMethod.GET} ,params = {"username","password"})
测试结果如下所示:
此时报错原因依然为在注解中设置了Parma属性值password,但是未能匹配到该值,所以出现错误,解决方法有两种,一种是我们在html文件中设置该参数,由浏览器自动为我们匹配,如下所示:
index.html:
<a th:href="@{/hello(username=${admin},password=${1234})}" >测试@RequestMapping注解的param属性</a> <a th:href="@{/hello(username='admin',password='12345')}">测试@RequestMapping注解的param属性</a>
重新部署项目,运行如下所示:
第二种方式是我们在地址栏通过手动添加缺失的参数,如下所示:
也可以成功进入到我们想访问的页面!
“!param”:表示当前所匹配请求的请求参数中一定不能携带param参数
假设此时我们将注解修改如下所示:
请求参数中不可以包含password,即使是只有password这个参数,没有值也不可以。
@RequestMapping(value = {"/hello","/abc"}, method= {RequestMethod.POST,RequestMethod.GET} ,params = {"username","!password"})
重新部署项目如下所示,我们手动在地址栏添加了password,虽然只是添加了该参数没有给定具体的值,结果也报错了。
“param=value”:表示当前所匹配请求的请求参数中必须携带param参数且值必须为value
假设此时我们将注解修改如下所示:
添加请求参数,并将其值设置为20
@RequestMapping(value = {"/hello","/abc"}, method= {RequestMethod.POST,RequestMethod.GET} ,params = {"username","!password","age=20"})
重新部署项目,如下所示,只有当注解中的设置的所有请求参数都被满足时,该请求才能被正确处理
“param!=value”:表示当前所匹配请求的请求参数中可以不携带param,若携带值一定不能是value
假设此时我们将注解修改如下所示:
添加新的请求参数gender,并设置其值不能为男
@RequestMapping(value = {"/hello","/abc"}, method= {RequestMethod.POST,RequestMethod.GET} ,params = {"username","!password","age=20","gender!=男"})
重新部署项目,测试如下所示:
手动添加其参数的值,gender的值可以是除了男以外的任何值,当然也可以不传值,或者不手动在地址栏添加该请求参数均可
@RequestMapping注解headers属性:
String[] headers() default {};
@RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射,它是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系,如下所示:
"header":要求请求映射所匹配的请求必须携带header请求头信息
"!header":要求请求映射所匹配的请求必须不能携带header请求头信息
"header=value":要求请求映射所匹配的请求必须携带header请求头信息且header=value
"header!=value":要求请求映射所匹配的请求必须携带header请求头信息且header!=value
若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到
例如:我们可以通过设置headers的referer属性来指明当前资源的来源地址,如下所示:
@RequestMapping(value = {"/hello","/abc"}, method= {RequestMethod.POST,RequestMethod.GET},headers = {"referer"})
重新部署项目,在浏览器中右击打开检查,如下所示为我们为点击该超链接的请求头信息:
此时我们点击该超链接,如下图所示,与上图不同之处在于多了一个Referer属性,而该属性的值即为该页面的来源地址
注意:响应头和请求头中的键不区分大小写,但是其键值区分大小写
@RequestMapping注解使用ant风格的路径:
?:表示任意的单个字符
举例如下:
**第一步:**编写新的测试方法,并将其RequestMapping的值设置为/a?a/test/ant
package AnnotationController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class JumpPageController { @RequestMapping("/a?a/test/ant") public String testAnt(){ return "success"; } }
第二步:在index.html中为其添加超链接
<a th:href="@{/aaa/test/ant}">测试@RequestMapping注解支持ant风格的路径</a><br>
将项目重新部署,测试结果如下:
点击该超链接:
通过我们在index.html文件中设置的路径,此时资源被正确的访问到,既然?是代表一个任意的字符,那么我们可以将a和a中键的位置任意选择一个符号来代替,可以是数字,可以是其他字母,当然也可以是特殊字符,但是需要注意不能是英文的问号,中文的问号是可以的,否则如下所示:
原因是**?前面是请求路径,问号后面是请求参数**,/a?a/test/ant并不会被当做一个完整的请求路径,其替代?的字符也不能是/,因为它代表地址的分隔符,表示新的一层目录
*:表示任意的0个或多个字符
举例如下:
此时我们将上述注解中的?替换为*,如下所示:
@RequestMapping("/a*a/test/ant")
将项目重新部署后,刷新浏览器页面如下所示:
无论是一个字符还是多个字符,都能成功的访问地址
而需要注意的是和?占位符相同,此时替代*的字符不可以是/或者?(英文),否则会出现404,原因和?的是相通的
**:表示任意层数的任意目录
举例如下:
将路径中的具体信息修改为如下所示:
@RequestMapping("/**/test/ant")
需要注意的是**只能写在//中,前后都不能有任何的字符
。
错误实例:
@RequestMapping("/a**a/test/ant") @RequestMapping("/**a/test/ant")
重新部署项目,测试结果如下所示:
/**/所代表的的目录我们既可以设置为一层,也可以设置为多层
但需要注意的是,即使可以设置任意层,但是目录中依然不可以包含有英文的问号,否则如下所示:
注意:在使用**
时,只能使用/**/xxx
的方式
@RequestMapping注解使用路径中的占位符:
通过路径传值的传统方式为:
/deleteUser?id=1
下面我们要介绍一种简单的方式:需要在@RequestMapping注解的value属性中所设置的路径中,使用{xxx}的方式表示路径中的数据,在通过@PathVariable注解,将占位符所标识的值和控制器方法的形参进行绑定,具体操作如下所示:
第一步:编写对应的控制器方法
package AnnotationController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class JumpPageController { //注意:有几个参数需要写几个{xxx}{xxx} @RequestMapping("/test/rest/{username}/{id}") //@PathVariable注解的值必须与@RequestMapping注解路径具体信息中的参数名称相一致,否则会出现500错误 public String testRest(@PathVariable("id") Integer id,@PathVariable("username") String username){ //获取对应参数的值 System.out.println("id:"+id+","+"username:"+username); return "success"; } }
第二步:在index.html文件中,设置其参数的具体值
注意这里值的顺序应与@RequestMapping注解中的参数顺序保持一致,否则会因为类型不匹配导致出现400错误
<a th:href="@{/test/rest/admin/1}">测试@RequestMapping注解value属性中的占位符</a><br>
重新部署项目,测试结果如下:
在浏览器栏显示了其参数的值
在控制台,也将对应的参数信息显示出来了