本文以纯后端的角度,去研究Spring Controller在各种情况的行为,及各种属性的作用。
实验准备
利用https://start.spring.io/快速生成一个开箱即用的小巧spring boot项目,无需进行复杂配置,非常适合进行研究实验使用。
若以下例子未说明,结果为下述代码所示。
@Controller@RequestMapping(value = "/terra")public class TestController { // 放置实验的Controller }
端口号在application.properties设置为9000
server.port=9000server.contextPath=/server.tomcat.uri-encoding=UTF-8
@RequestMapping
@RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。(抄)
一. value
value不设置和设置为""的情况下会产生冲突,编译会报错。
@RequestMapping() public String voidController1() { return "voidController1"; } @RequestMapping(value = "") public String voidController2() { return "voidController2"; }
两者可以看做等价,但是又存在区别。
value不设置的情况下,认为是默认控制器,此时不能令返回值=void,否则会在请求时报错
// TODO 需要翻源码了解为何报错
Circular view path [second]: would dispatch back to the current handler URL [/second] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
但是可以跳转到其他类下的路径,而value = "" 时则不会有这些问题.
@Controller@RequestMapping(value = "/second")public class SecondPageController { @RequestMapping() public String defaultController() { // 报错 // return "/terra"; return "/terra/test"; }}
于是乎可以做到类似以下的杂技般有趣但又没多大意义的跳转,需要注意的是默认控制器无法直接使用本类下的子路径,原因不明。
@Controller@RequestMapping(value = "/terra")public class TestController { @RequestMapping(value = "test") public String testController() { System.out.println("测试请求"); return "/terra"; } @RequestMapping() public String defaultController() { System.out.println("请求跳转到了默认请求控制器"); // 无法跳转 // return "/jump"; return "/terra/jump"; } @RequestMapping(value = "jump") public String jumpController() { System.out.println("请求跳转到了jump"); return "void"; } @RequestMapping(value = "void") public void voidController() { System.out.println("请求跳转到了void,终止"); }}
localhost:9000/terra/test 或者localhost:9000/second 请求控制台输出:测试请求请求跳转到了默认请求控制器请求跳转到了jump请求跳转到了void,终止
顺带一提value支持中文,猜想支持的范围=String.equal,但是一般不会有猿们这么用吧
@Controller@RequestMapping(value = "/中文")public class ZnTestController { @RequestMapping(value = "测试") public void testController() { System.out.println("测试请求"); }}
想写下一个内容的过程发现 value值中带不带“/”是等价的,下面两个test会报错,且用无斜杠与有斜杠的路径没有区别
@Controller@RequestMapping("value")public class ValueBindController { @RequestMapping(value = "/test") public void hasSlash() { System.out.println("有斜杠"); } @RequestMapping(value = "test") public void noneSlash() { System.out.println("无斜杠"); }}
路径貌似不支持其他特殊字符,如#value或/#value时无法与localhost:/#value/test进行匹配
......好吧正题
value支持使用占位符对url进行值绑定到参数上,如下列形式,这种即是REST风格的入参形式
@Controller@RequestMapping("value")public class ValueBindController { @RequestMapping(value = "printValue/{value}") public void printValue(@PathVariable String value) { // localhost:9000/value/printValue/1 System.out.println(value); } @RequestMapping(value = "printPrintValue/{value}") public void printPrintValue(@PathVariable("value") String printValue) { // localhost:9000/value/printPrintValue/1 System.out.println(printValue); } @RequestMapping(value = "printValues/{value1}/{value2}") public void printValues(@PathVariable String value1, @PathVariable Integer value2) { // localhost:9000/value/printValues/1/2 System.out.println(value1 + "/" + value2); } @RequestMapping(value = "printMap/{value1}/{value2}") public void printValues(@PathVariable Mapmap) { // localhost:9000/value/printValues/1/2 System.out.println(map.get("value1") + "/" + map.get("value2")); } @RequestMapping(value = "printList/{list}") public void printValues(@PathVariable List list) { // localhost:9000/value/printList/1,2,3 list.stream().forEach(k -> System.out.print(k + " ")); System.out.println(); }}
博客上有人提到过使用@PathVariable会产生截断问题,即value/{value},若输入为1.jpg,value =1。但是测试时发现非常非常的正常,版本问题么?
value支持多路径,如下,这种形式还可以解决当参数参数非必传时的问题
@Controller@RequestMapping(value = "mul")public class MulValueController { @RequestMapping(value = {"one", "two", "three"}) public void test() { // localhost:9000/mul/one // localhost:9000/mul/two // localhost:9000/mul/three System.out.println("多路径匹配"); } @RequestMapping(value = {"value/{value1}", "value/{value1}/{value2}"}) public void printValues(@PathVariable String value1, @PathVariable(required = false) String value2) { // localhost:9000/value/1 // localhost:9000/value/1/2 System.out.println(value1 + "/" + value2); }}
value亦支持正则表达式
@Controller@RequestMapping(value = "regex")public class RegexController { @RequestMapping(value = "tel/{number:^18[0-9]\\d{8}$}") public void telPhone(@PathVariable String number) { // 匹配以18开头的手机号码 // localhost:9000/regex/tel/18123456890 System.out.println(number); }}
但是复杂一些的正则会出一些莫名其妙的错误,原因不明,比如
^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$
会报下面这个异常。
The number of capturing groups in the pattern segment (^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\d{8}$) does not match the number of URI template variables it defines, which can occur if capturing groups are used in a URI template regex. Use non-capturing groups instead.
二. method
method用于指定请求类型,如GET,POST
路径相同类型不同的方法可以共存(value =value , method != method) ,
如下GET请求会到getController,POST请求会执行postController,其余类型的请求会执行allController
@RequestMapping(method = RequestMethod.GET) public String getController() { return "Get请求"; } @RequestMapping(method = RequestMethod.POST) public String postController() { return "Post请求"; } @RequestMapping() public String allController() { return "任意请求"; }
method支持多参数,这里用GET还是用POST都会调用这个方法
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}) public String getPostController() { return "GetOrPost请求"; }
待续