Java Spring MVC:在表单处理操作中有多个@ModelAttribute

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19244320/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 15:23:40  来源:igfitidea点击:

Spring MVC: Having multiple @ModelAttribute in form handling action

javaspringspring-mvcannotationsspring-annotations

提问by Rafa? Wrzeszcz

The context

上下文

I have a simple association between two entities - Categoryand Email(NtoM). I'm trying to create web interface for browsing and managing them. To browse the category and to add e-mails into that category I use controller wrapped with @RequestMappingwith category ID (UUID), so all controller actions are always taking place in context of category specified with path.

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

I use @ModelAttributeto pre-load context category for entire controller scope.

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

The problem

问题

This approach worked well for listing and for displaying the forms. However it fails on form submission - after debugging a little, I found out that form data overrides my category @ModelAttributeparameter.

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

In my code, in method save()the categoryis not really the model attribute loaded with addCategory()method, but is populated with form data (emailmodel is also populated, and that is correct).

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

I'm looking for the solution that will allow me to bind form data only to specific @ModelAttribute.

我正在寻找允许我将表单数据仅绑定到特定@ModelAttribute.

I've read in Spring MVC documentation that order of arguments matters, but I ordered them accordingly to examples and still it doesn't work like expected.

我在 Spring MVC 文档中读到参数的顺序很重要,但是我根据示例对它们进行了相应的排序,但它仍然无法像预期的那样工作。

The code

编码

Here is my controller:

这是我的控制器:

@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());
    }
}

Just in case here is also the form code:

以防万一这里也是表单代码:

<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>

Note:I don't want multiple @ModelAttributes to come from POST, just want to distinguish somehow form model from previously generated attribute(s).

注意:我不希望多个@ModelAttributes 来自 POST,只是想以某种方式将表单模型与以前生成的属性区分开来。

采纳答案by Kre?imir Nesek

I'm not sure I understand the problem entirely, but to me, it seems you want category object present in the model when you display the form, but don't want it to be changed with form post?

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

When you specify @ModelAttribute("categories") in argument list you basically tell spring MVC to bind form data to the annotated object using the parameter name "categories".

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

If you don't want the object to be bound just leave it out from the parameters list. If you need the original object in the handler method fetch it manually by calling addCategory and providing id mapped with @PathVariable:

如果您不想绑定对象,只需将其从参数列表中删除即可。如果您需要处理程序方法中的原始对象,请通过调用 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. If you register a converter that converts Long to Category using categoryService you can also put @PathVariable("categoryId") Category categoryto map Category object to path variable instead of UUID, if you'd like that take look at 7.5.5 Configuring a ConversionService)

(PS。如果您注册一个转换器,使用 categoryService 将 Long 转换为 Category,您还可以将@PathVariable("categoryId") Category categoryCategory 对象映射到路径变量而不是 UUID,如果您愿意,请查看7.5.5 配置 ConversionService

(EDIT: removed suggestion to name model differently as that will not help as noted in comments, and added example)

(编辑:删除了以不同方式命名模型的建议,因为这在评论中没有帮助,并添加了示例)

Personally, if I needed this kind of behavior (an object that needs to be present in the form when displaying the form, but not bound to it when form is posted) I would not use ModelAttribute annotated method to populate the model. Instead, I'd populate the model manually when displaying the form. That is a bit more code (well, one line actually) but is less magical and easier to understand.

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