[TOC]

问题起因:
关于 @RequestBody 注解使用的几点疑问:
事情的起因是这样的,入职新公司之后,查看代码,见到了一种写法,感到十分奇怪:
@PostMapping("/xxx/yyy")
public Result<Boolean> createXXX(PaymentServiceDTO serviceDTO, PaymentAccountDTO accountDTO) {
}
- 按照
Restful Http
请求来说,POST
请求的参数一般是放到Body
中的,这种写法我理解是不允许的。 - 其次,POST 请求中放入了两个请求对象,记得之前有同事告诉我
@RequestBody
用于 POST请求,作用是将请求中的 Body json 数据解析为对象类型,方便操作,但是@RequestBody
只能在一个Mapping
中使用一次。 - 后来还看到类似下面的代码,更增加了我的疑惑
@PostMapping("/xxx/yyy")
public Result<Boolean> createXXX(@RequestBody PaymentServiceDTO serviceDTO, PaymentAccountDTO accountDTO) {
}
- 该项目
POST
请求为什么会有上面的写法? -
@RequestBody
能否修饰多个对象? -
@RequestBody
修饰一个对象,但是像上面没有修饰的对象会怎么处理? - 如果像上面一样,两个对象都不用
@RequestBody
修饰,那么参数应该放在哪里?又是怎么解析为对象的?
上面已经十分明白,我还是挺佩服的,这里说下后三个问题的答案:
Answer:
-
@RequestBody
不能修饰多个对象,在一个方法中同时使用该注解,编译能通过,运行时会抛出异常 - 即使是
POST
请求,默认的参数类型也是application/x-www-form-urlencoded
,即参数作为 query param,放到 url 的后面。因此这种就好解释了
- 被
@RequestBody
修饰的对象,会填充 Body 中的参数 - 没有修饰的对象,会由
query param
中的参数进行填充
- 如果都不用,就都会由
query param
中进行解析,填充入各个对象中,唯一的问题就是对象属性名不能相同,相同的属性名会被填充相同的值。
问题测试:
下面是测试的示例:
1. 使用 @RequestBody 修饰正常的情况
后端代码:
@PostMapping("/normal")
public void printInfo1(@RequestBody User user) {
log.info("user -->" + user);
}
调用:
curl -X POST --header "Content-Type: application/json" --header "Accept: */*" -d "{
\"firstName\": \"firstname\",
\"lastName\": \"lastname\"
}" "http://localhost:8080/normal"
2019-05-10 19:20:22.920 INFO 2129 --- [io-8080-exec-10] com.example.demo.TestController : user -->User(id=0, firstName=firstname, lastName=lastname)
2. 不使用 @RequestBody 修饰
后端代码:
@PostMapping("/with_no_annotation")
public void printInfo2(User user) {
log.info("user -->" + user);
}
调用:
- 参数放到 Body 中
curl -v -X POST --header "Content-Type: application/json" --header "Accept: */*" -d "{
\"firstName\": \"firstname\",
\"lastName\": \"lastname\"
}" "http://localhost:8080/with_no_annotation"
2019-05-10 19:36:36.742 INFO 2129 --- [nio-8080-exec-5] com.example.demo.TestController : user -->User(id=0, firstName=null, lastName=null)
- 参数放到查询参数中
curl -v -X POST \
http://localhost:8080/with_no_annotation \
-d 'firstName=firstname&lastName=lastname'
2019-05-10 19:39:09.072 INFO 2129 --- [nio-8080-exec-1] com.example.demo.TestController : user -->User(id=0, firstName=firstname, lastName=lastname)
3. 传递两个对象,一个使用 @RequestBody 注解,一个不使用
后端代码:
@PostMapping("/with_one_have")
public void printInfo3(@RequestBody User user, ProductInfo productInfo) {
log.info("user -->" + user);
log.info("product-->" + productInfo);
}
调用:
- 参数一个放在 Body 中,一个放入到 query param中
curl -X POST \
'http://localhost:8080/with_one_have?productName=productName&productDesc=productDesc' \
-H 'Content-Type: application/json' \
-d '{
"firstName": "firstname",
"lastName": "lastname"
}'
2019-05-10 19:49:56.695 INFO 2129 --- [nio-8080-exec-2] com.example.demo.TestController : user -->User(id=0, firstName=firstname, lastName=lastname)
2019-05-10 19:49:56.695 INFO 2129 --- [nio-8080-exec-2] com.example.demo.TestController : product-->ProductInfo(id=0, productName=productName, productDesc=productDesc)
4. 两个对象参数都使用 @RequestBody 修饰
后端代码:
@PostMapping("/with_all_have_annotation")
public void printInfo4(@RequestBody User user, @RequestBody ProductInfo productInfo) {
log.info("user -->" + user);
log.info("product-->" + productInfo);
}
调用:
- 调用方法都没有办法写。。。。。。。
- 下面是使用一个Json作为参数传递
curl -X POST --header "Content-Type: application/json" --header "Accept: */*" -d "{
\"productName\":\"productName\",
\"productDesc\":\"productDesc\"
}" "http://localhost:8080/with_all_have_annotation"
2019-05-10 19:54:50.903 WARN 2129 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed]
5. 两个对象参数都不使用 @RequestBody修饰
后端代码:
@PostMapping("/with_all_no_annotation")
public void printInfo5(User user, ProductInfo productInfo) {
log.info("user -->" + user);
log.info("product-->" + productInfo);
}
调用:
出现问题当对象包含相同的字段时,会被覆盖为相同的值
curl -X POST \
'http://localhost:8080/with_all_no_annotation?id=1111&firstName=li&lastName=haifei&productName=productName&productDesc=productDesc'
2019-05-10 19:56:27.394 INFO 2129 --- [nio-8080-exec-7] com.example.demo.TestController : user -->User(id=1111, firstName=li, lastName=haifei)
2019-05-10 19:56:27.394 INFO 2129 --- [nio-8080-exec-7] com.example.demo.TestController : product-->ProductInfo(id=1111, productName=productName, productDesc=productDesc)
6. 总结
看完上面,应该已经明白上面问题的答案了。其实这里如果看过源码,应该很好就解决了,奈何这里没看,只能使用比较笨的方法。还是源码大法好啊!
网友评论