7

上下文

我在两个实体之间有一个简单的关联 -CategoryEmail(NtoM)。我正在尝试创建用于浏览和管理它们的 Web 界面。为了浏览类别并将电子邮件添加到该类别中,我使用使用@RequestMapping类别 ID (UUID) 包装的控制器,因此所有控制器操作始终发生在使用路径指定的类别的上下文中。

我用来@ModelAttribute为整个控制器范围预加载上下文类别。

问题

这种方法适用于列出和显示表单。但是它在提交表单时失败了——经过一点调试,我发现表单数据覆盖了我的类别@ModelAttribute参数。

在我的代码中,在方法save()中,category实际上并不是使用addCategory()方法加载的模型属性,而是填充了表单数据(email模型也被填充,这是正确的)。

我正在寻找允许我将表单数据仅绑定到特定的解决方案@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,只是想以某种方式将表单模型与先前生成的属性区分开来。

4

1 回答 1

4

我不确定我是否完全理解这个问题,但对我来说,当你显示表单时,你似乎希望模型中存在类别对象,但不希望它随着表单发布而改变?

当您在参数列表中指定 @ModelAttribute("categories") 时,您基本上是告诉 spring MVC 使用参数名称“categories”将表单数据绑定到带注释的对象。

如果您不想绑定对象,只需将其从参数列表中删除即可。如果您需要处理程序方法中的原始对象,请通过调用 addCategory 并提供使用 @PathVariable 映射的 id 手动获取它:

@RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(
    @ModelAttribute @Valid Email email,
    BindingResult result,
    Model model,
    @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") Category categoryCategory 对象映射到路径变量而不是 UUID,如果您愿意,请查看7.5.5 配置 ConversionService

(编辑:删除了以不同方式命名模型的建议,因为这将无济于事,如评论中所述,并添加了示例)

就个人而言,如果我需要这种行为(显示表单时需要在表单中出现的对象,但在发布表单时未绑定到它),我不会使用 ModelAttribute 注释方法来填充模型。相反,我会在显示表单时手动填充模型。那是更多的代码(嗯,实际上是一行),但不那么神奇且更容易理解。

于 2013-10-08T10:29:22.883 回答