【问题标题】:Spring MVC: Validate id of nested objectSpring MVC:验证嵌套对象的 id
【发布时间】:2021-08-18 09:39:41
【问题描述】:

主要问题

在我的 Web 应用程序中,使用 Spring/Hibernate/Thymeleaf 实现,我有两个域对象,所有者和宠物。 Pet 对象有一个(可选的)Owner 字段。在通过网络表单更新宠物期间,用户可以输入存在(在数据库中)或不存在的所有者 ID。

当用户使用现有的所有者 ID 更新宠物时,会找到现有的所有者对象并将其与宠物相关联。 但是,当用户使用数据库中不存在的所有者 ID 更新宠物时,返回的宠物对象(在“POST”控制器方法中)具有空所有者对象。此外,由于宠物域中的所有者字段可以为空,因此不会引发验证错误。

最终,这可能会导致用户提交错误的值,认为他们添加了一个所有者,只是为了让宠物的更新实际发生在一个空所有者的情况下(如果没有正确捕获)。

我的问题是,在执行父对象更新之前验证嵌套对象的 id 存在的推荐方法是什么。

进一步的想法:

  • 我看到了一个相关问题:spring mvc nested model validation,建议在其中添加自定义验证器。在我的情况下,添加一个自定义验证器,它将在 PetController 中运行并验证 Pet/Owner 对象,我认为这不起作用。返回的宠物对象包含一个空所有者对象,这是一个完全有效的状态。我需要在数据绑定期间进行验证。一旦我们进入控制器,绑定后,所有者对象为空,并且不会对表单值进行验证。
  • 我已经通过添加自定义格式化程序(从字符串到所有者)对其进行了一些工作。如果无法找到所有者 id,则此格式化程序将返回一个新的所有者对象,其 id 为“未找到”(或在这种情况下为一些数字/整数等价物)。在我的控制器中,我可以检查具有“错误 id”的所有者并引发错误。但是,不返回对象的默认行为似乎更安全,因为我的自定义格式化程序实际上返回了一个无效对象(这可能会导致其他问题?)。

代码

@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Pet {

    @Id
    private int id;

    private String name;

    @ManyToOne
    @Valid
    private Owner owner;
    
}

@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Owner {

    @Id
    @NotNull
    @NonNull
    private int id;

    private String firstName;

    private String lastName;

}
@Controller
public class PetController {

    @Autowired
    PetRepository petRepository;

    @RequestMapping("editPet")
    @GetMapping
    public String editPet(Model model) {
        Pet pet =  petRepository.getById(1);
        model.addAttribute("pet", pet);
        return "editPet";
    }


    @RequestMapping("updatePet")
    @PostMapping
    public String updatePet(@Validated Pet pet, BindingResult bindingResult) {
        petRepository.save(pet); // if the user submits an invalid owner id, the returned pet will have a null owner 
        // object, but no error raised.
        return "editPet";
    }
}
}

【问题讨论】:

    标签: java spring validation spring-mvc


    【解决方案1】:

    我已经想出了一种以干净的方式完成上述操作的方法,在这里发布以防它帮助其他人。

    解决方案是使用 Spring 的类型 conversion。从文档中获得的两个要点是,首先,要创建一个转换器,我们只需要实现 Converter 接口。其次,要报告无效的源值(例如我上面的问题陈述中的错误所有者 ID),应该引发 IllegalArgumentException。

    在转换期间引发的任何 IllegalArgumentExceptions 都将在控制器方法的 BindingResult 中报告。这意味着不需要额外的代码/努力来报告控制器中的非法值。

    StringToOwner 转换器的实现:

    首先,检查存储库中是否存在具有给定 ID 的可选所有者。如果没有具有给定 ID 的所有者,则会引发异常。无需检查 null;根据documentation,对于每次调用转换器,源参数保证不为空。

    @Component
    public class StringToOwner implements Converter<String, Owner> {
    
        @Autowired
        OwnerRepository ownerRepository;
    
        @Override
        public Owner convert(String s) {
    
            Optional<Owner> optOwner = ownerRepository.findById(Integer.parseInt(s));
            if (optOwner.isEmpty()) {
                throw new IllegalArgumentException("Illegal owner id");
            }
            return optOwner.get();
        }
    }
    

    随后,我们可以编辑 Controller 以检查绑定错误:

        @RequestMapping("updatePet")
        @PostMapping
        public String updatePet(@Validated Pet pet, BindingResult bindingResult, Model model) {
            if (bindingResult.hasErrors()) {
                model.addAttribute("pet", pet);
                return "editPet"; // Errors can be reported in the generated page
            }
            petRepository.save(pet);
            return "success";
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-21
      • 1970-01-01
      • 2012-08-28
      • 2012-09-22
      • 2014-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多