Spring boot验证问题:理解MethodArgumentNotValidException

问题描述 投票:0回答:1

我在使用 Spring Boot 验证表单时遇到问题。我已经找到了问题的原因(并且知道可能的解决方案),但我想了解为什么会发生这种情况。

在 post 方法到达的控制器中我有:

@RequestMapping(value = "/add", method = RequestMethod.POST)
public String processAddNewEvent(@Valid @ModelAttribute("newEvent") EventDto newEvent, @ModelAttribute("imageFile") MultipartFile imageFile, BindingResult result, HttpServletRequest request) {

    if(result.hasErrors()) {
        return "addEvent";
    }
    
    return "redirect:/events";
}

我想要的只是验证 newEvent 对象。如果有错误,我会再次显示“addEvent”表单并显示错误消息。

但是在这个方法中,我从表单接收两个参数:newEvent 对象和一个文件(imageFile)。在这种情况下,一旦调用此函数,我就会收到 MethodArgumentNotValidException。但是,如果我删除 imageFile 并仅接收 newEvent 对象,则不会引发异常并且验证可以正常工作。

也就是说,如果我的控制器看起来像这样,则验证有效并且我没有例外:

@RequestMapping(value = "/add", method = RequestMethod.POST)
public String processAddNewEvent(@Valid @ModelAttribute("newEvent") EventDto newEvent, BindingResult result, HttpServletRequest request) {
    if(result.hasErrors()) {
        return "addEvent";
    }
    
    return "redirect:/events";
}

所以,我的问题是,验证可以在一个对象上进行,而不是在另一个对象上进行吗?我不应该从表单中收到两个不同的对象吗?正确的做法是什么?

感谢并抱歉我的英语!

我解决这个问题的想法是将图像文件放入 newEvent 类中,从而只接收一个对象。我仍然不知道它是否会起作用。我只是想好好理解这个问题

java spring-boot validation
1个回答
0
投票

出现您的问题是因为 Spring 尝试验证两个对象(

newEvent
imageFile
),因为
@Valid
上存在
newEvent
,但只有
newEvent
符合验证条件。
MultipartFile
并不意味着使用标准 bean 验证机制(如
@Valid
@NotNull
)进行验证,因此会导致
MethodArgumentNotValidException

是否可以对一个对象进行验证而不对另一个对象进行验证? 是的,验证只能应用于 newEvent 对象。您正确地在

@Valid
上使用
newEvent
,这会触发该特定对象的验证。
MultipartFile
,但是,应该单独处理,而不应用验证注释。

我不应该从表单中接收两个不同的对象吗?是的,您可以接收两个单独的对象:

newEvent
(DTO)和
MultipartFile
(文件)。只要您不尝试以与
MultipartFile
相同的方式验证
newEvent
对象,就可以单独处理它们。

解决方案 更改参数顺序,如下所示:

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String processAddNewEvent(
    @Valid @ModelAttribute("newEvent") EventDto newEvent, 
    BindingResult result, 
    @ModelAttribute("imageFile") MultipartFile imageFile, 
    HttpServletRequest request) {

    if (result.hasErrors()) {
        return "addEvent"; // Return the form view with error messages
    }

    // Handle the imageFile separately (e.g., save the file, validate its size/type)
    // Validate the imageFile
    if (!imageFile.isEmpty()) {
        // Example: Check the file size (e.g., max 2MB)
        long maxFileSize = 2 * 1024 * 1024; // 2 MB
        if (imageFile.getSize() > maxFileSize) {
            result.rejectValue("imageFile", "error.imageFile", "File size exceeds the 2MB limit.");
            return "addEvent";
        }

        // Example: Check the file type (e.g., only accept JPEG and PNG)
        String contentType = imageFile.getContentType();
        if (!"image/jpeg".equals(contentType) && !"image/png".equals(contentType)) {
            result.rejectValue("imageFile", "error.imageFile", "Only JPEG and PNG files are accepted.");
            return "addEvent";
        }

        // Save the image file to a specific directory
        try {
            String uploadDirectory = "/path/to/upload/directory"; // Replace with actual path
            String fileName = imageFile.getOriginalFilename();
            Path path = Paths.get(uploadDirectory + File.separator + fileName);
            Files.copy(imageFile.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            result.rejectValue("imageFile", "error.imageFile", "Failed to save the file. Please try again.");
            return "addEvent";
        }
    }
    
    return "redirect:/events";
}
© www.soinside.com 2019 - 2024. All rights reserved.