当表单中仅使用少量属性时,将完整的模型对象发布到控制器


问题内容

我在某处已经读到Spring
MVC,如果表单不包含@ModelAttribute注释设置的模型对象的所有属性,则返回NULL是一种预期的行为。我该如何使用不具有模型对象的所有字段的表单,仍然将整个但已更新的对象接收回控制器的post方法。

我意图的简短示例代码:

控制器的一部分:

....

    @RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
    public String editPost(Model model, @PathVariable Integer id) {
        model.addAttribute("editPost", bPostService.getPost(id));
        return "editPost";
    }

    @RequestMapping(value = "/edit/{id}", method = RequestMethod.POST)
    public String editProcessPost(Model model, @PathVariable Integer id, @ModelAttribute BPost editPost) {
        bPostService.updatePost(editPost);
        model.addAttribute("posts", bPostService.getPosts());
        return "redirect:/";
    }

....

hibernate映射的实体:

@Entity
@Table(name = "sometable")
public class BPost {

    @Id
    @GeneratedValue
        @Column(name = "id")
    private int id;

    @Column(name = "title")
    private String title;

    @Column(name = "description")
    private String description;

    @Column(name = "text")
    private String text;

    @Column(name = "anothertext")
    private String anothertext;

    // getters and setters
}

JSP视图的一部分:

<form:form method="POST" modelAttribute="editPost" action="${pageContext.request.contextPath}/secure/post/edit/${editPost.id}">
<table>
<tbody>
    <tr>
        <td>title:</td>
        <td><form:input path="title"></form:input></td>
    </tr>
    <tr>
        <td>description:</td>
        <td><form:input path="description"></form:input></td>
    </tr>
    <tr>
        <td>text:</td>
        <td><form:input path="text"></form:input></td>
    </tr>
    <tr>
        <td><input value="Edit" type="submit"></td>
        <td></td>
    </tr>
</tbody>
</table>
</form:form>

如您所见,JSP上未使用“ anothertext”属性,但我希望它不变地返回到控制器的POST方法。那可能吗?

我知道有人可能已经问过这个问题,但是我找不到答案。

谢谢!


问题答案:

您可能不希望将实体用作可能存在安全隐患的表单后备对象。例如,可以伪造一个恶意请求来设置一些不需要的属性。

因此,通常最好为每个要处理的表单创建一个显式的表单支持对象。这将需要您编写更多的代码,但同时也消除了一些常见的问题(例如您遇到的问题)。

使用表单支持对象时,处理程序看起来像:

请注意,我将BPost参数更改为BPostForm

@RequestMapping(value = "/edit/{id}", method = RequestMethod.POST)
public String editProcessPost(Model model, @PathVariable Integer id, @ModelAttribute BPostForm editPost) {

    // fetch the original post
    BPost post = bPostService.findById(editPost.getId());

    // set the properties
    post.setTitle(editPost.getTitle());
    post.setDescription(editPost.getDescription());
    post.setText(editPost.getText());

    // update
    bPostService.updatePost(post);

    model.addAttribute("posts", bPostService.getPosts());
    return "redirect:/";
}

PS使用bPostService.getPosts()添加帖子到模型,并立即返回重定向似乎很没有意义;)

[编辑]验证

您可以使用Hibernate批注通过声明性验证或在中设置自定义验证器来验证表单支持对象WebdataBinder

hibernate注释

使用Hibernate批注时,可以将任何批注放在字段或getter上。为了使这些验证生效,您需要做两件事。

  1. 注册一个验证器bean org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
  2. 使用注释处理程序中表单支持对象的参数@valid

例: public String editProcessPost(Model model, @PathVariable Integer id, @ModelAttribute @Valid BPostForm editPost, BindingResult result)

请注意,使用验证需要BindingResult在参数列表中出现,并且必须直接在支持对象之后。这BindingResult将是所有验证错误的容器。

定制验证器

自定义验证器还需要更多工作。您将需要先编写自己的内容。

MyPostValidator extends org.springframework.validation.Validator

编写验证器后,您可以将其添加到中WebDataBinder

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.setValidator(new MyPostValidator());
}