5

When only displaying a part of a Spring MVC model bean, is there a way to only update in the model what comes back from the browser?

Let's say we have a User class (public properties only in this example, of course):

public class User {
  public String firstName;
  public String lastName;
  public String email;
  public Date subscriptionExpiration;
}

Now I display the first three properties as input fields in a JSP and want to update the object in the database accordingly. Only these 3 parameters should be updated, NOT the fourth one. One way to accomplish this would be

@RequestMapping("/user/{userId}", method=RequestMethod.POST)
public String saveChanges(@PathVariable userId, User user, Model model) {
  User oldUser = User.loadFromDB(userId);
  oldUser.firstName = user.firstName;
  oldUser.lastName = user.lastName;
  oldUser.email = user.email;
  oldUser.saveToDB();

  model.addAttribute("user", oldUser);
}

but this would mean hardcoding all properties that could change, which I don't like too much.

Is there any way to determine what fields to update based on what the user was allowed to change? That mechanism should be smarter than just assuming that everything that's in the request parameters may be changed, otherwise any savvy user could manually inject additional fields into a request.

Using @Entity(dynamicUpdate=true) does not solve the problem either in my eyes, as I'm not getting the whole User object back in the request, and doing so would open up many security holes.

Am I missing a nice feature in Spring or is there any other way to conceptually solve for this problem? Any hints are greatly appreciated!

4

6 回答 6

3

请参阅此问题以了解如何使用 @InitBinder 来允许/防止某些模型字段被 Spring 绑定以请求参数。这允许确保subscriptionExpiration不能通过注入请求参数来修改。

请参阅文档,了解如何在方法和方法参数上使用 @ModelAttribute 注释从数据库加载用户并将其添加到模型中,然后再调用 @RequestMapping 方法,并在 @RequestMapping 方法被调用时使用请求参数填充此用户RequestMapping 方法被调用。这允许从数据库中获取用户并让 Spring 使用来自请求的 ne 值填充它。您所要做的就是验证用户的新状态并将其保存到数据库中。

于 2012-12-22T21:48:05.950 回答
2

我认为方法

BeanUtils.copyProperties(Object source, Object target, String[] ignoreProperties) 

做你想做的事。它将所有属性从一个对象复制到另一个对象,而不会触及 中定义的属性String[] ignoreProperties

将给定源 bean 的属性值复制到给定目标 bean 中,忽略给定的“ignoreProperties”。

注意:源类和目标类不必匹配,甚至不必相互派生,只要属性匹配即可。源 bean 公开但目标 bean 未公开的任何 bean 属性都将被静默忽略。

这只是一种方便的方法。对于更复杂的传输需求,请考虑使用完整的 BeanWrapper。

API 文档

于 2012-12-22T21:48:12.657 回答
0

一种选择是利用表单中的隐藏字段。一个有缺点的选项,但它适用于某些情况

于 2014-03-23T20:52:23.210 回答
0

您可以先从数据库中读取对象,然后再绑定请求。您可以在FuWeSta-Sample找到一个示例。

它使用一个必须由 Spring 初始化的helper-bean 。

于 2014-03-03T11:03:35.637 回答
0

您正在寻找的好功能是@ModelAttribute。我假设您的表单也会发布用户 ID。

public class UserController {

    @ModelAttribute
    public User getUser(@RequestParam(value = "id", required = false) Long id) {
        return User.loadFromDB(id);
    }

    @RequestMapping("/user/{id}", method=RequestMethod.POST)
    public String saveChanges(@ModelAttribute User user, BindingResult bindingResult, Model model) {

        if (bindingResult.hasErrors()) {
            // return ...
        }

        //  model.asMap().clear();

        user.saveToDB();
        // return ...
    }
}
于 2014-10-21T16:50:41.083 回答
0

我有同样的问题,最后通过添加一个类型转换器来解决它。

public class AbstractDocumentConverter implements Converter<String, AbstractDocument> {

    private AbstractDocumentRepository abstractDocumentRepository;

    public AbstractDocumentConverter(AbstractDocumentRepository abstractDocumentRepository) {
        this.abstractDocumentRepository = abstractDocumentRepository;
    }

    @Override
    public AbstractDocument convert(String id) {
        return abstractDocumentRepository.findOne(id);
    }
}

我使用 mongodb, AbstractDocument 是我所有文档的超类,在控制器中我添加 @ModelAttribute("id") 。

@RequestMapping("save")
public String save(@ModelAttribute("id") SportGame game) {
    sportGameService.save(game);
    return "redirect:/game/list";
}

它首先将 id 转换为模型(在我的转换器中,它通过 id 从 db 中找到一个),然后从请求参数中填充模型字段。

于 2018-06-22T13:40:11.983 回答