叙述
我们知道无论是 Get 请求还是 Post 请求,Controller 这边都可以定义一个实体类来接收这些参数。而 @ControllerAdvice 结合 @InitBinder 还能实现请求参数预处理,即将表单中的数据绑定到实体类上时进行一些额外处理。
描述
假设我们有如下两个实体类 User 和 Book:
public class User {
private String name;
private Integer age;
// 省略getter/setter
}
public class Book {
private String name;
private Float price;
// 省略getter/setter
}
如果在 Contoller 上需要接收两个实体类的数据,接收方法可以这么定义:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(User user, Book book) {
return "name:" + user.getName() + " | age:" + user.getAge() + "<br>"
+ "name:" + book.getName() + " | price:" + book.getPrice();
}
}
但由于两个实体类中都有 name 属性,那么参数传递时就会发生混淆。
解决方案
使用 @ControllerAdvice 结合 @InitBinder 即可解决上面的问题,这里我们创建一个全局的参数预处理配置。
代码说明:
- 第一个 @InitBinder("user") 表示该方法是处理 Controller 中 @ModelAttribute("user") 对应的参数。
- 第二个 @InitBinder("book") 表示该方法是处理 Controller 中 @ModelAttribute("book") 对应的参数。
- 这两个方法中给相应的 Filed 设置一个前缀。
补充说明:在 WebDataBinder 对象中,除了可以设置前缀,还可以设置允许、禁止的字段、必填字段以及验证器等等。
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
@ControllerAdvice
public class GlobalConfig {
@InitBinder("user")
public void init1(WebDataBinder binder) {
binder.setFieldDefaultPrefix("user.");
}
@InitBinder("book")
public void init2(WebDataBinder binder) {
binder.setFieldDefaultPrefix("book.");
}
}
然后 Controller 中方法的参数添加 @ModelAttribute 注解:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(@ModelAttribute("user") User user,
@ModelAttribute("book") Book book) {
return "name:" + user.getName() + " | age:" + user.getAge() + "<br>"
+ "name:" + book.getName() + " | price:" + book.getPrice();
}
}
最后浏览器请求参数中添加相应的前缀,即可成功区分出 name 属性: