目录

Spring Boot - 映射请求

笔记

因为需要多个 URL 能同时映射进入一个方法(一个 RequstMapping),所以学习了 Spring Boot 的 Controller 映射请求方式

2022-12-22 @Young Kbt

# @RequestMapping 介绍

Spring 4.3 中引进了 @GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping},来帮助简化常用的 HTTP 方法的映射,并更好地表达被注解方法的语义。

从命名约定我们可以看到每个注释都是为了处理各自的传入请求方法类型,即 @GetMapping 用于处理请求方法的 GET 类型,@PostMapping 用于处理请求方法的 POST 类型等。

@RequestMapping 注解可以在控制器类或其中的方法的级别上使用:

@RestController  
@RequestMapping("/home")  
public class IndexController {  
    @RequestMapping("/")  
    String get() {  
        // 请求地址:hostname:port/home/  
        return "Hello from get";  
    }  
    @RequestMapping("/index")  
    String index() {  
        // 请求地址:hostname:port/home/index/  
        return "Hello from index";  
    }  
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

优先匹配类上的 @RequestMapping,然后再进入类里,匹配方法上的 @RequestMapping

# URL 映射配置方法

映射单个 URL:@RequestMapping("")@RequestMapping(value = "")

映射多个 URL:@RequestMapping({"xxx","yyy"})@RequestMapping(value = {"xxx","yyy"})

路径开头是否加斜杠/均可,建议加上,如:@RequestMapping("")@RequestMapping("/") 效果一样,都是根路径。

# 五种映射方式

@RequestMapping 一共有五种映射方式:

# 标准映射

标准URL 映射是最简单的一种映射。

@RequestMapping("/hello")
@RequestMapping({"/hello","/world/test"})
1
2

# Ant 风格

Ant 通配符有三种:

  • ?:匹配当前目录任何单字符
  • *:匹配当前目录任意数量的字符(含 0 个)
  • **:匹配任意数量的目录(含 0 个)

当前目录就是 /xx/,两个 / 围起来的是一个目录。

@RequestMapping("/?/hello/")
@RequestMapping("/*/hello")
@RequestMapping("/**/hello")
1
2
3

# 占位符

可以通过一个或多个 {} 占位符映射。

若 URL 中的 userId 是纯数字,那么使用 @PathVariable 做绑定时,可以根据自己的需求将方法参数类型设置为 Long、Integer、String 等。

@PathVariable("") 不能简写为 @PathVariable

@RequestMapping("/user/{userId}/show")

public User show(@PathVariable("userId") Long userId) {
    User user = userService.getById(userId);
    return user;
}
1
2
3
4
5
6

# 限制请求方法

HTTP 请求中最常用的请求方法是 GET、POST,还有其他的一些方法,如:DELETE、PUT、HEAD 等。

@RequestMapping(value="/hello", method=RequestMethod.POST)
1

如需限制多个请求方法,以大括号包围,逗号隔开即可,例如:

@RequestMapping(value="/hello", method={RequestMethod.GET,RequestMethod.POST})
1

# 限制请求参数

如:请求中必须带有 userId 参数。

参数的限制规则如下:

  • params="userId" 请求参数中必须包含 userId
  • params="!userId" 请求参数中不能包含 userId
  • params="userId!=1" 请求参数中必须包含 userId,但不能为 1
  • params={"userId","userName"} 必须包含 userId 和 userName 参数
  • params={"userId=1","userName!=2"} 必须包含 userId 和 userName 参数,且 userId 值必须为 1,userName 值必须不为2

# 处理多个 URL

可以将多个请求映射到一个方法上去,只需要添加一个带有请求路径值列表的 @RequestMapping 注解就行了。

@RestController  
@RequestMapping("/home")  
public class IndexController {  

    @RequestMapping(value = {"", "/page", "page*", "view/*","**/msg"})  
    String indexMultipleMapping() {  
        return "Hello from index multiple mapping.";  
    }  
} 
1
2
3
4
5
6
7
8
9

如你在这段代码中所看到的,@RequestMapping 支持统配符以及 ANT 风格的路径。前面这段代码中,如下的这些 URL 都会由 indexMultipleMapping() 来处理:

  • localhost:8080/home
  • localhost:8080/home/
  • localhost:8080/home/page
  • localhost:8080/home/pageabc
  • localhost:8080/home/view/
  • localhost:8080/home/view/view

# 处理生产和消费对象

使用 @RequestMapping 注解的 produces 和 consumes 这两个元素来缩小请求映射类型的范围。

了能用请求的媒体类型来产生对象, 你要用到 @RequestMapping 的 produces 元素再结合着 @ResponseBody 注解。

你也可以利用 @RequestMapping 的 comsumes 元素再结合着 @RequestBody 注解用请求的媒体类型来消费对象。

下面这段代码就用到的 @RequestMapping 的生产和消费对象元素:

@RestController  
@RequestMapping("/home")  
public class IndexController { 
    
    @RequestMapping(value = "/prod", produces = {"application/JSON"})  
    @ResponseBody  
    String getProduces() {  
        return "Produces attribute";  
    }  

    @RequestMapping(value = "/cons", consumes = {"application/JSON", "application/XML"})  
    String getConsumes() {  
        return "Consumes attribute";  
    }  
} 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在这段代码中,getProduces() 处理方法会产生一个 JSON 响应,getConsumes() 处理方法可以同时处理请求中的 JSON 和 XML 内容。

# 处理消息头

@RequestMapping 注解提供了一个 header 元素来根据请求中的消息头内容缩小请求映射的范围。

在可以指定 header 元素的值,用 myHeader = myValue 这样的格式:

@RestController  
@RequestMapping("/home")  
public class IndexController {  
    
    @RequestMapping(value = "/head", headers = {"content-type=text/plain"})  
    String post() {  
        return "Mapping applied along with headers";  
    }  
} 
1
2
3
4
5
6
7
8
9

在上面这段代码中,@RequestMapping 注解的 headers 属性将映射范围缩小到了 post() 方法。有了这个,post() 方法就只会处理到 /home/head 并且 content-typeheader 被指定为 text/plain 这个值的请求。

你也可以像下面这样指定多个消息头:

@RestController  
@RequestMapping("/home")  
public class IndexController {
    
    @RequestMapping(value = "/head", headers = {"content-type=text/plain", "content-type=text/html"})
    String post() {  
        return "Mapping applied along with headers";  
    }  
}
1
2
3
4
5
6
7
8
9

这样,post() 方法就能同时接受 text/plain 还有 text/html 的请求了。

# 处理请求参数

@RequestMapping 直接的 params 元素可以进一步帮助我们缩小请求映射的定位范围。使用 params 元素,你可以让多个处理方法处理到同一个 URL 的请求, 而这些请求的参数是不一样的。

你可以用 myParams = myValue 这种格式来定义参数,也可以使用通配符来指定特定的参数值在请求中是不受支持的。

@RestController  
@RequestMapping("/home")  
public class IndexController {
    @RequestMapping(value = "/fetch", params = {"personId=10"})  
    String getParams(@RequestParam("personId") String id) {  
        return "Fetched parameter using params attribute = " + id;  
    }  
    @RequestMapping(value = "/fetch", params = {"personId=20"})  
    String getParamsDifferent(@RequestParam("personId") String id) {  
        return "Fetched parameter using params attribute = " + id;  
    }  
}  
1
2
3
4
5
6
7
8
9
10
11
12

在这段代码中,getParams()getParamsDifferent() 两个方法都能处理相同的一个 URL:/home/fetch,但是会根据 params 元素的配置不同而决定具体来执行哪一个方法。

例如,当 URL 是 /home/fetch?id=10 的时候,getParams() 会执行,因为 id 的值是 10。对于 /home/fetch?personId=20 这个URL, getParamsDifferent() 处理方法会得到执行,因为 id 值是 20。

# 处理动态 URL

@RequestMapping 注解可以同 @PathVariable 注解一起使用,用来处理动态的 URI,URI 的值可以作为控制器中处理方法的参数。你也可以使用正则表达式来只处理可以匹配到正则表达式的动态 URI。

@RestController  
@RequestMapping("/home")  
public class IndexController {  
    @RequestMapping(value = "/fetch/{id}", method = RequestMethod.GET)  
    String getDynamicUriValue(@PathVariable String id) {  
        System.out.println("ID is " + id);  
        return "Dynamic URI parameter fetched";  
    }  
    @RequestMapping(value = "/fetch/{id:[a-z]+}/{name}", method = RequestMethod.GET)  
    String getDynamicUriValueRegex(@PathVariable("name") String name) {  
        System.out.println("Name is " + name);  
        return "Dynamic URI parameter fetched using regex";  
    }  
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在这段代码中,方法 getDynamicUriValue() 会在发起到 localhost:8080/home/fetch/10 的请求时执行。这里 getDynamicUriValue() 方法 id 参数也会动态地被填充为 10 这个值。

方法 getDynamicUriValueRegex() 会在发起到 localhost:8080/home/fetch/category/shirt 的请求时执行。不过,如果发起的请求是 /home/fetch/10/shirt 的话,会抛出异常,因为这个 URI 并不能匹配正则表达式。

@PathVariable@RequestParam的运行方式不同。你使用 @PathVariable 是为了从 URI 里取到查询参数值。换言之,你使用 @RequestParam 是为了从 URI 模板中获取参数值。

# 默认的处理方法

在控制器类中,你可以有一个默认的处理方法,它可以在有一个向默认 URI 发起的请求时被执行。

下面是默认处理方法的示例:

@RestController  
@RequestMapping("/home")  
public class IndexController {  
    @RequestMapping()  
    default () {  
        return "This is a default method for the class";  
    }  
}
1
2
3
4
5
6
7
8

在这段代码中,向 /home 发起的一个请求将会由 default() 来处理,因为注解并没有指定任何值。

# 快捷方式

Spring 4.3 引入了方法级注解的变体,也被叫做 @RequestMapping 的组合注解。组合注解可以更好的表达被注解方法的语义。它们所扮演的角色就是针对 @RequestMapping 的封装,而且成了定义端点的标准方法。

例如,@GetMapping 是一个组合注解,它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一个快捷方式。 方法级别的注解变体有如下几个:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

如下代码展示了如何使用组合注解:

@RestController  
@RequestMapping("/home")  
public class IndexController {  
    @GetMapping("/person")  
    public @ResponseBody ResponseEntity < String > getPerson() {  
        return new ResponseEntity < String > ("Response from GET", HttpStatus.OK);  
    }  
    @GetMapping("/person/{id}")  
    public @ResponseBody ResponseEntity < String > getPersonById(@PathVariable String id) {  
        return new ResponseEntity < String > ("Response from GET with id " + id, HttpStatus.OK);  
    }  
    @PostMapping("/person")  
    public @ResponseBody ResponseEntity < String > postPerson() {  
        return new ResponseEntity < String > ("Response from POST method", HttpStatus.OK);  
    }  
    @PutMapping("/person")  
    public @ResponseBody ResponseEntity < String > putPerson() {  
        return new ResponseEntity < String > ("Response from PUT method", HttpStatus.OK);  
    }  
    @DeleteMapping("/person")  
    public @ResponseBody ResponseEntity < String > deletePerson() {  
        return new ResponseEntity < String > ("Response from DELETE method", HttpStatus.OK);  
    }  
    @PatchMapping("/person")  
    public @ResponseBody ResponseEntity < String > patchPerson() {  
        return new ResponseEntity < String > ("Response from PATCH method", HttpStatus.OK);  
    }  
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

在这段代码中,每一个处理方法都使用 @RequestMapping 的组合变体进行了注解。尽管每个变体都可以使用带有方法属性的 @RequestMapping 注解来互换实现, 但组合变体仍然是一种最佳的实践,这主要是因为组合注解减少了在应用程序上要配置的元数据,并且代码也更易读。

# 请求参数

不同的请求方式如何接收参数,可以看 Spring Boot - 请求参数接收

更新时间: 2024/01/17, 05:48:13
最近更新
01
JVM调优
12-10
02
jenkins
12-10
03
Arthas
12-10
更多文章>