利用Idea的HttpClient向SpringBoot发起各种请求参数。后端使用@RequestParam
,@ModelAttribute
,@PathVariable
,@RequestBody
,@RequestPart
去接收。
Get 请求是没有 body 的,参数都是放在 url 上面。
根据在 url 参数格式不同,一共有下面这几种
- Path Variable
- Query
- Matrix Variable(不常用)
1 2 3
| GET http://localhost:7720/user/find/123/起凡 \ / 这里的"123"和"起凡"是参数
|
这种传参格式就如同名字一样"路径变量",参数在路径上。
针对这种传参方式后端可以这么接受。
1 2 3 4 5 6 7 8
| // 在路径中用id占位,代表这个地方将会是参数 @GetMapping("find/{id}/{username}") // 方法上面通过占位名称得到参数 public R<Boolean> pathVariable(@PathVariable String id, @PathVariable String username) { log.info(id); log.info(username); return R.ok(true); }
|
1 2
| ### GET http://localhost:7720/user/find?username=起凡&password=123456
|
在路径的最后用 “?” 隔开要传输的参数。 通过 “&” 分割多个 key=value
1 2 3 4 5 6 7
| @GetMapping("find") // 通过key接受参数 public R<Boolean> get(@RequestParam String username, @RequestParam String password) { log.info(username); log.info(password); return R.ok(true); }
|
如果现在需要做一个用户搜索功能,前端会传 pageNum(页数), pageSize(每页大小), keyword(关键词),startTime(创建日期)。
1 2
| GET http://localhost:7720/user/search?pageNum=1&pageSize=10 &keyword=起凡&startTime=Fri Apr 29 2022 21:16:50
|
使用 @RequestParam
,我们需要在接口上接受四个参数,这样会显得接口过于庞大,不雅观。
1 2 3 4 5
| @GetMapping("search") public R<Boolean> search(@RequestParam Integer pageSize, @RequestParam Integer pageNum, @RequestParam String keyword, @RequestParam Date startTime) {
return R.ok(true); }
|
改造一下,我们可以创建一个 SearchDto
1 2 3 4 5 6 7
| @Data public class SearchDto { Integer pageNum; Integer pageSize; String keyword; Date startTime; }
|
通过 @ModelAttribute
就可以接受多个 query 参数
1 2 3 4 5
| @GetMapping("search") public R<Boolean> search(@ModelAttribute SearchDto searchDto) { log.info(searchDto.toString()); return R.ok(true); }
|
刚刚我们做的是用户搜索功能,现在假设我们需要做文章搜索功能。文章搜索的过滤条件比用户搜索多了一个 category(文章类别)。
1 2
| GET http://localhost:7720/article/search?pageNum=1&pageSize=10 &keyword=spring boot&startTime=Fri Apr 29 2022 21:16:50&category=科技
|
这时我们可以再创建一个 ArticleSearchDto 然后继承 SearchDto
1 2 3 4
| @Data public class ArticleSearchDto extends SearchDto{ String category; }
|
然后在文章搜索接口上接受 ArticleSearchDto
1 2 3 4 5
| @GetMapping("search") public R<Boolean> search(@ModelAttribute ArticleSearchDto searchDto) { log.info(searchDto.toString()); return R.ok(true); }
|
但是这样真的方便吗?因为多出一个字段就要再建立一个类,是不是有点麻烦了。我们完全可以把@ModelAttribute
和 @RequestParam
结合起来使用
1 2 3 4 5 6 7
| @GetMapping("search") // @RequestParam 获取新增的过滤条件 public R<Boolean> search(@ModelAttribute SearchDto searchDto, @RequestParam String category) { log.info(searchDto.toString()); log.info(category); return R.ok(true); }
|
POST 请求的参数可以放在 url 上,也可以放在 body 种。
POST 请求的 body 格式大概可以分为两种,
- 一种是只能传输一个对象
- 一种是可以传输多个对象。
application/json,application/x-www-form-urlencoded 属于第一种
multipart/form-data 属于第二种。
通过上面的例子可以知道,@RequestParam
和 @ModelAttribute
可以获取 query 里面的参数。
实际上这两个注解还可以获取 application/x-www-form-urlencoded 里面的参数。
之前是通过 get 请求进行文章搜索。现在改造一下,前端通过 post 请求进行文章搜索。
1 2 3 4
| POST http://localhost:7720/article/search?author=起凡&pageSize=10 Content-Type: application/x-www-form-urlencoded
pageNum=1&keyword=spring boot&startTime=Fri Apr 29 2022 21:16:50&category=科技
|
我特意把 author 和 pageSize 放在 query 里面,看看 springboot 是否能接受到参数。
1 2 3 4 5 6 7
| @PostMapping("search") public R<Boolean> search(@ModelAttribute SearchDto searchDto, @RequestParam String category, @RequestParam String author) { log.info(searchDto.toString()); log.info("category = " + category); log.info("author = " + author); return R.ok(true); }
|
输出结果
1 2 3
| SearchDto(pageNum=1, pageSize=10, keyword=spring boot, startTime=Fri Apr 29 21:16:50 CST 2022) category = 科技 author = 起凡
|
结果分析:
- pageSize 和 pageNum 分别在 query 和 form 里面,但是依然被
@ModelAttribute
正确绑定到 SearchDto 的 pageSize 和
pageNum 属性上。
- category 和 author 分别在 query 和 form 里面, 但是依然被
@RequestParam
正确的获取到。
还是上面的文章搜索功能,但是这次前端会把搜索条件用 json 格式发送
1 2 3 4 5 6 7 8 9 10 11
| ### POST http://localhost:7720/article/search?category=科技 Content-Type: application/json jctoken: bef84c6b-e160-4590-82a9-191f0c8a17fa
{ "pageNum": 1, "pageSize": 10, "startTime": "2022-04-29T22:52:00", "keyword": "spring boot" }
|
后台接收
1 2 3 4 5 6 7
| @PostMapping("search") // 通过@RequestParam获取query中的额外参数 public R<Boolean> search(@RequestBody SearchDto searchDto, @RequestParam String category) { log.info(searchDto.toString()); log.info("category = " + category); return R.ok(); }
|
因为文章搜索多出了 category 这个参数,后端的 SearchDto 中没有这个属性,如果放在 json 里面 @RequestBody
就无法接收这个参数。所以把
category 放在 query 中然后通过 @RequestParam
接收。
@RequestBody
还可以接收嵌套的 json对象,只需要后端创建相应的类就可以了
这种格式的 body 有许多 part,每个 part 通过一个分隔符分割。
body 格式如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ## boundary 参数告诉请求接受者,要使用 "aBoundaryString" 对 body 进行分割。 Content-Type: multipart/form-data; boundary=aBoundaryString
## 发送请求时,使用 "aBoundaryString" 进行分割, ## 这个分隔符一定要和上面规定的一样 --aBoundaryString ## name是每一部分的名称,后端可以通过@RequestParam("myFile") 获取到这个部分 Content-Disposition: form-data; name="myFile"; filename="img.jpg" ## 二进制数据的格式是什么 Content-Type: image/jpeg
(二进制数据) --aBoundaryString ## name是每一部分的名称,后端可以通过@RequestParam("text") 获取到这个部分 Content-Disposition: form-data; name="text"
(二进制文本数据,可以是json,xml,文本) --aBoundaryString (more subparts) ## 结尾分割符 需要在末尾加上 "--" --aBoundaryString--
|
现在前端想要上传一张图片,图片的名称为 123.png,接受参数为 file。
1 2 3 4 5 6 7 8 9
| POST http://localhost:7720/test/upload1 Content-Type: multipart/form-data; boundary=起凡分隔符
--起凡分隔符 Content-Disposition: form-data; name="file"; filename="123.png" Content-Type: image/png
(文件数据) --起凡分隔符--
|
后端接收
1 2 3 4 5
| @PostMapping("upload1") public R<Boolean> upload(@RequestParam("file") MultipartFile multipartFile) { log.info(multipartFile.getOriginalFilename()); return R.ok(true); }
|
前端现在想批量上传文件,后端应该如何接受呢?
因为文件的数量是不固定的,并且 name
参数也是未知的,后端不能像上面那样以这种方式接收 @RequestParam("file1")
,@RequestParam("file2")
…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| POST http://localhost:7720/test/upload2 Content-Type: multipart/form-data; boundary=起凡分隔符
--起凡分隔符 ## name 随机取值 Content-Disposition: form-data; name="asdasdas"; filename="123.png" Content-Type: image/png
(二进制数据) --起凡分隔符 ## name 随机取值 Content-Disposition: form-data; name="asdasdasssdasdsada"; filename="mytext.txt" Content-Type: text/plain
(二进制数据) --起凡分隔符--
|
后端接收
1 2 3 4 5 6 7 8
| @PostMapping("upload2") public R<Boolean> upload2(@RequestParam Map<String, MultipartFile> multipartFileMap) { multipartFileMap.forEach((name, file) -> { log.info("name = " + name); log.info("filename = " + file.getOriginalFilename()); }); return R.ok(true); }
|
输出结果
1 2 3 4 5 6
| name = asdasdas filename = 123.png ----------------------- name = asdasdasssdasdsada filename = mytext.txt -----------------------
|
分析结果:
后端通一个 map 把前端传过来的参数封装起来,map 的 key 是 name,map 的 value 是文件内容
假设前端想要发送一张图片,并且要附上这张图片的描述信息,那后端应该如何接收?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| POST http://localhost:7720/test/upload3 Content-Type: multipart/form-data; boundary=起凡分隔符
--起凡分隔符 Content-Disposition: form-data; name="file"; filename="123.png" Content-Type: image/png
(二进制数据) --起凡分隔符 Content-Disposition: form-data; name="meta-data"; Content-Type: application/json;
{ "createTime": "2022-04-29T10:52:00", "location" : "福建省 泉州市 华侨大学" } --起凡分隔符--
|
后端接收
1 2 3 4 5
| @Data public class FileMetaData { Date createTime; String location; }
|
1 2 3 4 5 6
| @PostMapping("upload3") public R<Boolean> upload3(@RequestPart("file") MultipartFile multipartFile, @RequestPart("meta-data") FileMetaData fileMetaData) { log.info("filename = " + multipartFile.getOriginalFilename()); log.info(fileMetaData.toString()); return R.ok(true); }
|
输出结果
1 2
| filename = 123.png FileMetaData(createTime=Fri Apr 29 18:52:00 CST 2022, location=福建省 泉州市 华侨大学)
|
结果分析:
- meta-data 对应的 part 是 json 格式,
@RequestPart
自动把它变成 FileMetaData。
- file 对应的 part 是文件,
@RequestPart
自动把它变成 MultipartFile