先来说说URL
是什么,它是定义网络上某个特定的资源,说明它位于何处,以及如何获取。
它的通用语法格式是:
://:@:/;?#
具体到Spring MVC
处理http(s)
协议的场景中,其应用语法得到了简化:
http(s)://:/?#
http
或者https
,这是肯定的,不用多说
参数,所有需要的URL
参数都采用
参数传递
是锚点参数,其作用范围仅限于前端浏览器代理等,请求的时候是不能传递给后端服务器的,这是出于网络安全的考虑再考虑到HTTP
协议请求报文格式的三个部分(请求行、请求头、请求体):
基本就可以有个总结,Spring MVC
能够获取HTTP
请求参数的位置应该有四个:
中的path
参数或者query
参数
中的请求头参数
中的请求体参数cookies
中的参数,其本质也是请求头传参接下来的问题就是了解Spring MVC
的编码规范了。
/*
请求报文:
GET /user/zhangsan/12 HTTP/1.1
Host: localhost:8080
*/
@GetMapping("/user/{name}/{age}")
public void path(@PathVariable String name, @PathVariable String age) {
logger.info("name = {}, age = {}", name, age);
}
// name = zhangsan, age = 12
因为path
参数位于URL
当中,Spring MVC
就采用占位符{xxx}
的方式,将参数名称和参数值对应,再结合@PathVariable
注解实现参数变量的注入。
采用path
传递参数,特别适合设计restful
接口。
还有另外一种稍微奇葩一点的写法,实际用处可能不大,仅做了解:
/*
请求报文:
GET /userzhangsan/12 HTTP/1.1
Host: localhost:8080
*/
@GetMapping("/user{name}/{age}")
public void path(@PathVariable String name, @PathVariable String age) {
logger.info("name = {}, age = {}", name, age);
}
// name = zhangsan, age = 12
注意区别,这种写法在URL
路径中,userzhangsan
是连起来的,中间没有/
划分,Spring MVC
接收的时候,就要写成user{name}
形式,相当于加了个限制,所有name
参数的前缀都必须是user
字符串。
/*
请求报文:
GET /user?name=zhangsan&age=12 HTTP/1.1
Host: localhost:8080
*/
@GetMapping("/user")
public void query(String name,String age) {
logger.info("name = {}, age = {}", name, age);
}
// name = zhangsan, age = 12
请求中采用了query
参数格式,其值为?name=zhangsan&age=12
,以?
标记作为query
参数的开始,以&
分割多个参数。
方法参数没有加Spring
注解,默认注解是@RequestParam
,加不加都行。
加上@RequestParam
注解的好处是,方法参数名定义更自由:
@GetMapping("/user")
public void path1(@RequestParam(value = "name") String person,@RequestParam String age) {
logger.info("name = {}, age = {}", person, age);
}
示例中将方法参数分开写成了两个,如果query
参数太多,还可以定义一个Java Bean
,将对应query
参数作为Bean
的字段,然后以Bean
作为方法参数:
@Data
public class User {
private String name;
private String age;
}
@GetMapping("/user")
public void query(User user) {
logger.info("name = {}, age = {}", user.getName(), user.getAge());
}
需要注意的是,这里绝对不能再加注解@RequestParam
,因为这里方法参数名称是user
,跟query
参数名称是不对应的。
不加注解,Spring MVC
可以自动将多个query
参数注入到user
对象属性当中,属性名需要和参数名保持一致。
/*
请求报文:
GET /user?name=zhangsan&age=12 HTTP/1.1
Host: localhost:8080
*/
@GetMapping("/user")
public void header(@RequestHeader(value = "Host") String host) {
logger.info("host = {}", host);
}
// host = localhost:8080
这是获取某个具体请求头参数的便捷方法,主要就是根据注解@RequestHeader
达到目的。
如果要获取所有请求头,那还是通过最原始的HttpServletRequest
对象实现吧:
@GetMapping("/user")
public void header(HttpServletRequest request) {
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
logger.info("{} = {}", key, request.getHeader(key));
}
}
HTTP
协议中除了POST
和PUT
方法具有请求体以外,其它方法都没有请求体。所以,这一节内容只适用于POST
和PUT
请求。
通过path
和query
还有header
,可以传递一些简单的参数,如果要传递的参数比较复杂,还是放在请求体中比较好处理。
因为请求体可以承载复杂的参数传递,所以请求体的数据类型有多种分类,常用的三种:
application/x-www-form-urlencoded
application/json
multipart/form-data
他们都适用于简单或复杂文本数据的传递,可如果参数中包含文件,则必须使用第三种form-data
格式。
/*
请求报文:
POST /user HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Content-Length: 20
name=zhangsan&age=12
*/
@PostMapping("/user")
public void body(String name, String age) {
logger.info("name = {}, age = {}", name, age);
}
// name = zhangsan, age = 12
这里的参数在body
里面,是application/x-www-form-urlencoded
类型,类似的,通过方法参数名和请求参数名的对应绑定关系,就可以得到参数值。
有一点需要特别注意!如果query
和body
里面包含同名参数,那么将会同时得到两处参数,很多时候,这并不是我们想要的结果:
/*
请求报文:
POST /user?name=lisi&age=16 HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded
Content-Length: 20
name=zhangsan&age=12
*/
@PostMapping("/user")
public void body(String name, String age) {
logger.info("name = {}, age = {}", name, age);
}
// name = lisi,zhangsan, age = 16,12
@PostMapping("/user")
public void body(User user) {
logger.info("name = {}, age = {}", user.getName(), user.getAge());
}
// name = lisi,zhangsan, age = 16,12
将两个独立的方法参数封装到一个Java Bean
中,结果也是一样的。
如果出现这种情况,我认为这属于在Spring
领域内对HTTP
协议的滥用,应该纠正一下我们的使用规范。
传递参数可以在query
和body
两个部分二选一,但是尽量还是不要同时使用,如果一定要使用,最好也不要出现同名参数。
由此可见Spring MVC
也有其不足之处,它对HTTP
协议的使用无形中添加了很多的限制。
/*
请求报文:
POST /user?name=lisi&age=16 HTTP/1.1
Host: localhost:8080
Content-Type: application/json
Content-Length: 41
{
"name": "zhangsan",
"age": 12
}
*/
@PostMapping("/user")
public void body(User user) {
logger.info("name = {}, age = {}", user.getName(), user.getAge());
}
// name = lisi, age = 16
@PostMapping("/user")
public void body(@RequestBody User user) {
logger.info("name = {}, age = {}", user.getName(), user.getAge());
}
// name = zhangsan, age = 12
这里写了两个方法,一个加了@RequestBody
注解,一个没加注解,通过日志可以看出,取值的结果是不一样的。
没加注解的从
中取值注入了,加了注解的从
中取值注入了。
/*
请求报文:
POST /user?name=lisi&age=16 HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Length: 21196
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="name"
zhangsan
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="age"
12
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="1.jpg"
Content-Type: image/jpeg
(data)
----WebKitFormBoundary7MA4YWxkTrZu0gW
*/
@PostMapping("/user")
public void body(String name, String age, @RequestParam("file") MultipartFile file, HttpServletRequest request) {
logger.info("name = {}, age = {}, file = {}", name, age, file.getOriginalFilename());
}
// name = zhangsan,lisi, age = 12,16, file = 1.jpg
@PostMapping("/user")
public void body(User user, @RequestParam("file") MultipartFile file) {
logger.info("name = {}, age = {}, file = {}", user.getName(), user.getAge(), file.getOriginalFilename());
}
// name = zhangsan,lisi, age = 12,16, file = 1.jpg
在这种请求体类型中,不能使用注解@RequestBody
,同样也会存在
和
同时取值注入的问题。
/*
请求报文:
GET /user/zhangsan/12 HTTP/1.1
Host: localhost:8080
Cookie: JSESSIONID=3jd6f92h47syx64h58f2nduyso1nz63a; id=12345678
*/
@GetMapping("/user")
public void cookies(@CookieValue("JSESSIONID") String sessionId) {
logger.info("sessionId = {}", sessionId);
}
// sessionId = 3jd6f92h47syx64h58f2nduyso1nz63a
获取cookies
也是加个注解@CookieValue
就可以了。
如果想要获取所有的cookies
,还是需要从HttpServletRequest
对象中获取:
/*
请求报文:
GET /user/zhangsan/12 HTTP/1.1
Host: localhost:8080
Cookie: JSESSIONID=3jd6f92h47syx64h58f2nduyso1nz63a; id=12345678
*/
@GetMapping("/user")
public void cookies(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
logger.info("{} = {}", cookie.getName(),cookie.getValue());
}
}