我在两个实体之间有一个简单的关联 – 类别和电子邮件(NtoM).我正在尝试创建用于浏览和管理它们的Web界面.要浏览类别并将电子邮件添加到该类别中,我使用带有类别ID(UUID)的@RequestMapping包装的控制器,因此所有控制器操作始终在使用path指定的类别的上下文中进行.
我使用@modelattribute为整个控制器范围预加载上下文类别.
问题
这种方法适用于列表和显示表单.但是它在表单提交时失败 – 在稍微调试之后,我发现表单数据会覆盖我的类别@modelattribute参数.
在我的代码中,在方法save()中,类别实际上并不是使用addCategory()方法加载的模型属性,而是填充了表单数据(电子邮件模型也已填充,这是正确的).
我正在寻找能够将表单数据仅绑定到特定的@modelattribute的解决方案.
我在Spring MVC文档中读到了参数的顺序很重要,但是我根据示例对它们进行了相应的排序,但它仍然没有像预期的那样工作.
这是我的控制器:
@Controller @RequestMapping("/emails/{categoryId}") public class EmailsController { @modelattribute("category") public Category addCategory(@PathVariable UUID categoryId) { return this.categoryService.getCategory(categoryId); } @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Set.class,"categories",new CategoriesSetEditor(this.categoryService)); } @RequestMapping(value = "/create",method = RequestMethod.GET) public String createForm(@modelattribute Category category,Model model) { // here everything works,as there is just a single @modelattribute return "emails/form"; } @RequestMapping(value = "/save",method = RequestMethod.POST) public String save( @modelattribute @Valid Email email,BindingResult result,Model model,@modelattribute("category") Category category ) { // saving entity,etc // HERE! problem is,that response is bound BOTH to `email' and `category' model attributes // and overrides category loaded in `addCategory()' method return String.format("redirect:/emails/%s/",category.getId().toString()); } }
以防万一这里也是表单代码:
<form:form action="${pageContext.request.contextPath}/emails/${category.id}/save" method="post" modelAttribute="email"> <form:hidden path="id"/> <fieldset> <label for="emailName"><spring:message code="email.form.label.Name" text="E-mail address"/>:</label> <form:input path="name" id="emailName" required="required"/> <form:errors path="name" cssClass="error"/> <label for="emailRealName"><spring:message code="email.form.label.RealName" text="Recipient display name"/>:</label> <form:input path="realName" id="emailRealName"/> <form:errors path="realName" cssClass="error"/> <label for="emailIsActive"><spring:message code="email.form.label.IsActive" text="Activation status"/>:</label> <form:checkBox path="active" id="emailIsActive"/> <form:errors path="active" cssClass="error"/> <form:checkBoxes path="categories" element="div" items="${categories}" itemValue="id" itemLabel="name"/> <form:errors path="categories" cssClass="error"/> <button type="submit"><spring:message code="_common.form.Submit" text="Save"/></button> </fieldset> </form:form>
注意:我不希望多个@modelattributes来自POST,只是想以某种方式区分表单模型与先前生成的属性.
解决方法
在参数列表中指定@modelattribute(“categories”)时,基本上告诉spring MVC使用参数名称“categories”将表单数据绑定到带注释的对象.
如果您不希望绑定对象,请将其从参数列表中删除.如果您需要处理程序方法中的原始对象,请通过调用addCategory并提供与@PathVariable映射的id来手动获取它:
@RequestMapping(value = "/save",method = RequestMethod.POST) public String save( @modelattribute @Valid Email email,@PathVaribale("categoryId") UUID categoryId ) { // saving entity,etc return String.format("redirect:/emails/%s/",categoryId.toString()); //if category object is needed and not just id then fetch it with Category c = addCategory(categoryId). }
(PS.如果您注册使用categoryService将Long转换为Category的转换器,您还可以将@PathVariable(“categoryId”)类别类别映射到路径变量而不是UUID,如果您愿意,请查看7.5.5 Configuring a ConversionService)
(编辑:删除建议以不同的方式命名模型,因为这将无法帮助,如评论中所述,并添加了示例)
就个人而言,如果我需要这种行为(在显示表单时需要在表单中出现的对象,但在发布表单时不对其进行绑定)我不会使用ModelAttribute带注释的方法来填充模型.相反,我会在显示表单时手动填充模型.这是更多的代码(实际上是一行),但不那么神奇,更容易理解.