问题
Hello I have question about @ModelAttribute annotation. As i understand, we use @ModelAttribute in method arguments to get data from the model. But it's quite hard to understand clearly when and how its used. (Code samples are from Spring in Action 5 book) Why in this case in the code below in public String processOrder() method we do not use @ModelAttribute annotation on @Valid Order order
@Controller
@RequestMapping("/orders")
@SessionAttributes("order")
public class OrderController {
private OrderRepository orderRepo;
public OrderController(OrderRepository orderRepo) {
this.orderRepo = orderRepo;
}
@GetMapping("/current")
public String orderForm(@AuthenticationPrincipal User user,
@ModelAttribute Order order) {
if (order.getDeliveryName() == null) {
order.setDeliveryName(user.getFullname());
}
//following conditions
return "orderForm";
}
@PostMapping
public String processOrder(@Valid Order order, Errors errors, // <<< Here
SessionStatus sessionStatus,
@AuthenticationPrincipal User user) {
if (errors.hasErrors()) {
return "orderForm";
}
order.setUser(user);
orderRepo.save(order);
sessionStatus.setComplete();
return "redirect:/";
}
}
but in this case, DesignTacoController class, @ModelAttribute on a method processDesign() is used on @Valid Taco taco:
@Slf4j
@Controller
@RequestMapping("/design")
public class DesignTacoController {
@PostMapping
public String processDesign(@Valid @ModelAttribute("design") Taco design, // <<< Here
Errors errors, Model model) {
if (errors.hasErrors()) {
return "design";
}
// Save the taco design...
// We'll do this in chapter 3
log.info("Processing design: " + design);
return "redirect:/orders/current";
}
And then in the next chapter author removes @ModelAttribute from processDesign() method from the same DesignTacoController class.
@Controller
@RequestMapping("/design")
@SessionAttributes("order")
@Slf4j
public class DesignTacoController {
@ModelAttribute(name = "order")
public Order order() {
return new Order();
}
@ModelAttribute(name = "design")
public Taco design() {
return new Taco();
}
@PostMapping
public String processDesign(
@Valid Taco taco, Errors errors, // <<< Here
@ModelAttribute Order order) {
log.info(" --- Saving taco");
if (errors.hasErrors()) {
return "design";
}
Taco saved = tacoRepo.save(taco);
order.addDesign(saved);
return "redirect:/orders/current";
}
And in this code snippet(from the code above):
@PostMapping
public String processDesign(
@Valid Taco taco, Errors errors, // <<< Here
@ModelAttribute Order order) {
....
}
quote from book: "The Order parameter is annotated with @ModelAttribute to indicate that its value should come from the model and that Spring MVC shouldn’t attempt to bind request parameters to it." This I don't understand what author meant here, because in all tutorials it is said that when @ModelAttribute is used as a method arguments,it binds request parameters to it. Binds the form data with a POJO bean, model attribute is populated with data from a form submitted.
回答1:
The documentation is pretty clear on this:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods
@ModelAttribute
For access to an existing attribute in the model (instantiated if not present) with data binding and validation applied. See @ModelAttribute as well as Model and DataBinder.
Note that use of @ModelAttribute is optional (for example, to set its attributes). See “Any other argument” at the end of this table.
.
Any other argument
If a method argument is not matched to any of the earlier values in this table and it is a simple type (as determined by BeanUtils#isSimpleProperty, it is a resolved as a @RequestParam. Otherwise, it is resolved as a @ModelAttribute.
So essentially it is optional. You may wish to use just to make it explicit that that is how the argument is resolved or you may need to use if binding should not happen (by specifying binding = false
) See futher: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html. It is normally my preference to specify it regardless.
回答2:
This wasn't clear to me either.
Here we need specify the name if the model attribute. Because in our view we assume it is named "design" and not "taco".
@PostMapping
public String processDesign(@Valid @ModelAttribute("design") Taco design, Errors errors) {
If we rename the Taco class to Design ...
We don't need to specify the name if the model attribute.
It will be deduced from the simple name of the class.
com.example.Design
-> "design"
@PostMapping
public String processDesign(@Valid Design design, Errors errors) {
See the javadoc for ModelAttribute:
The default model attribute name is inferred from the declared attribute type (i.e. the method parameter type or method return type), based on the non-qualified class name: e.g. "orderAddress" for class "mypackage.OrderAddress", or "orderAddressList" for "List".
来源:https://stackoverflow.com/questions/58330323/when-we-must-use-modelattribute-and-how-it-works