在表单中只使用少量属性时,将完整的模型对象发布到控制器 [英] Posting a complete model object to the controller when only few attributes are used in a form

查看:87
本文介绍了在表单中只使用少量属性时,将完整的模型对象发布到控制器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我读过的地方是,对于spring mvc,如果表单没有包含由@ModelAttribute标注设置的模型对象的所有属性,则返回NULL是一种预期的行为。 S如何使用不包含模型对象所有字段的表单,并仍然将整个对象更新回控制器的post方法。



我的意图的简短示例代码:

控制器的一部分:

  .... 

@RequestMapping(value =/ edit / {id},method = RequestMethod.GET)
public String editPost(Model model,@PathVariable Integer id) {
model.addAttribute(editPost,bPostService.getPost(id));
返回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());
返回重定向:/;
}

....

映射的实体hibernate:

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

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

@Column(name =title)
私人字符串标题;

@Column(name =description)
私有字符串描述;

@Column(name =text)
private String text;

@Column(name =anothertext)
private String anothertext;

// getters and setters
}

部分JSP视图:

 < form:form method =POSTmodelAttribute =editPostaction =$ {pageContext.request .contextPath} /安全/后/编辑/ $ {} editPost.id> 
< table>
< tbody>
< tr>
< td> title:< / td>
< td>< form:input path =title>< / form:input>< / td>
< / tr>
< tr>
< td>说明:< / td>
< td>< form:input path =description>< / form:input>< / td>
< / tr>
< tr>
< td>文字:< / td>
< td>< form:input path =text>< / form:input>< / td>
< / tr>
< tr>
< td>< input value =Edittype =submit>< / td>
< td>< / td>
< / tr>
< / tbody>
< / table>
< / form:form>

正如您可以看到JSP上没有使用anothertext属性,但是我不想它不会返回到控制器的POST方法。这可能吗?



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



感谢!

解决方案

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



因此,最好为每个表单创建一个明确的表单支持对象。它会要求你编写更多的代码,但它也会消除一些常见的问题(比如你正在使用的那些)。

当使用表单支持对象时,你的处理器看起来更多例如:



请注意,我将 BPost 参数改为 BPostForm $ b $ pre $ @RequestMapping(value =/ edit / {id},method = RequestMethod.POST)
public String editProcessPost(Model model,@PathVariable Integer id,@ModelAttribute BPostForm editPost){

//获取原始文章
BPost post = bPostService.findById (editPost.getId());

//设置属性
post.setTitle(editPost.getTitle());
post.setDescription(editPost.getDescription());
post.setText(editPost.getText());

// update
bPostService.updatePost(post);

model.addAttribute(posts,bPostService.getPosts());
返回重定向:/;
}

P.s。使用 bPostService.getPosts()向模型添加帖子并立即返回重定向似乎毫无意义;)



验证



您的表单支持对象可以使用Hibernate批注的声明性验证或中的自定义验证器设置进行验证。 WebdataBinder



Hibernate注释



使用Hibernate注释时,字段或getter上的注释。


  1. 注册验证器bean org.springframework 。

  2. 使用 @valid 在您的处理程序中注释窗体支持对象的参数。 。


$ b 示例: public String editProcessPost(Model model,@PathVariable Integer id,@ModelAttribute @Valid BPostForm editPost ,BindingResult结果)



请注意,使用验证需要存在 BindingResult 参数列表,它需要直接在后备对象之后。这个 BindingResult 将是所有验证错误的容器。



自定义验证器



自定义验证程序有点多。你需要自己写第一个。

MyPostValidator extends org.springframework.validation.Validator



在编写验证器之后,您可以将它添加到 WebDataBinder

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


I've read somewhere that for spring mvc, it is a expected behavior to get back NULL in case a form does not contain all the attributes of the model object set by the @ModelAttribute annotiation. S how can I use forms that don't have all the fields of the model object and still recieve the whole but updated object back to the post method of the controller.

A short example code of my intention:

Part of the controller:

....

    @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:/";
    }

....

The entity mapped by 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
}

Part of the JSP view:

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

As you can see the "anothertext" attribute is not used on the JSP, but I wan't it unchanged returned to the POST method of the controller. Is that possible?

I know some one probably already asked this question, but I cant find the answer to that.

Thank!

解决方案

You might not want to use the entity as a form backing object which could have security implications. For example an malicious request could be forged to set some unwanted properties.

Therefor it's better in general to create a explicit form backing object for each form to process. It will require you to write more code but it also negates some common problems (like the one you're having).

When using a form backing object your handler looks more like:

Note that I altered the BPost argument to 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:/";
}

P.s. Using bPostService.getPosts() to add posts to the model and immediately return a redirect seems rather pointless ;)

[EDIT] Validation

Your form backing object can be validated using declarative validation using the Hibernate annotations or settings a custom validator in the WebdataBinder.

Hibernate annotations

When using the Hibernate annotations you can place any annotation on a field or getter. For these validations to kick in you'll need to do two things.

  1. Register a validator bean org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.
  2. Annotate the form backing object's argument in your handler with @valid.

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

Note that using validation needs a BindingResult to be present in the argument list and it needs to be directly after the backing object. This BindingResult will be a container for all validation errors.

Custom validator

A custom validator is a bit more work. You will need to write your own first.

MyPostValidator extends org.springframework.validation.Validator

After writing the validator you can add it to the WebDataBinder.

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

这篇关于在表单中只使用少量属性时,将完整的模型对象发布到控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆