繁体   English   中英

使用两个对象进行Spring Form验证

[英]Spring Form Validation with Two Objects

Spring Boot应用程序遇到了一个复杂的问题,我现在一直试图解决这个问题,我希望有人可以帮助我。 我删除了项目的所有其他部分,并尽量使其尽可能简单。 如果你去localhost:8080,会有一个带有两个文本框的表单,可以输入两个名称,还有一个Submit按钮。 第一个名称将存储在Nominee对象中,第二个名称将存储在Submitter对象中。 单击“提交”时,它将对字段执行验证,以确保它们都不为空。 我将发布下面的代码并在最后解释我的问题。

Application.java

@SpringBootApplication
@EnableJms
@EnableWebMvc
public class Application {

    public static void main(String[] args) throws Exception {
        // Launch the application
        SpringApplication.run(Application.class, args);
    }
}

WebController.java

@Controller
public class WebController extends WebMvcConfigurerAdapter {
    protected static final Logger LOG = LoggerFactory.getLogger(WebController.class);

    @InitBinder("nominee")
    protected void initNomineeBinder(WebDataBinder binder) {
        binder.setValidator(new NomineeValidator());
    }

    @InitBinder("submitter")
    protected void initSubmitterBinder(WebDataBinder binder) {
        binder.setValidator(new SubmitterValidator());
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/success").setViewName("success");
    }

    @RequestMapping(value="/", method=RequestMethod.GET)
    public String showForm(Model model) {
        model.addAttribute("nominee", new Nominee());
        model.addAttribute("submitter", new Submitter());
        return "form";
    }

    @RequestMapping(value="/", method=RequestMethod.POST)
    public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee,
                                  @ModelAttribute(value="submitter") @Valid Submitter submitter,
                                  BindingResult bindingResult, @Valid Model model) {
        LOG.info("Nominee to string: " + nominee.toString());
        LOG.info("Submitter to string: " + submitter.toString());
        LOG.info("bindingResult to string: " + bindingResult.toString());
        if (bindingResult.hasErrors()) {
            return "form";
        }

        return "redirect:/success";
    }
}

Nominee.java

import lombok.Data;

@Data
public class Nominee {
    private String name;
}

NomineeValidatior.java

public class NomineeValidator implements Validator {

    public boolean supports(Class clazz) {
        return Nominee.class.equals(clazz);
    }

    public void validate(Object object, Errors errors) {
        ValidationUtils.rejectIfEmpty(errors, "name", "name", "This field is empty.");
    }
}

Submitter.java

import lombok.Data;

@Data
public class Submitter {
    private String sname;
}

SubmitterValidator.java

public class SubmitterValidator implements Validator {

    public boolean supports(Class clazz) {
        return Submitter.class.equals(clazz);
    }

    public void validate(Object object, Errors errors) {
        ValidationUtils.rejectIfEmpty(errors, "sname", "sname", "This field is empty.");
    }
}

form.html

 <html> <body> <form role="form" th:action="@{/}" method="post"> <h2>Nominee details</h2> <table> <tr> <td>Name:</td> <td> <input type="text" th:field="${nominee.name}"/> </td> <td><p th:if="${#fields.hasErrors('nominee.name')}" th:errors="${nominee.name}">Empty field</p></td> </tr> </table> <h2>Your details</h2> <table> <tr> <td>Your name:</td> <td> <input type="text" th:field="${submitter.sname}"/> </td> <td><p th:if="${#fields.hasErrors('submitter.sname')}" th:errors="${submitter.sname}">Empty field</p></td> </tr> </table> <div> <button type="submit">Submit nomination</button> </div> </form> </body> </html> 

success.html

<html><body>Form successfully submitted.</body></html>

如果我将第一个文本字段留空(并填写或不填写第二个文本字段),屏幕上会显示一条错误消息,内容如下:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback. Tue May 12 13:10:17 AEST 2015 There was an unexpected error (type=Bad Request, status=400). Validation failed for object='nominee'. Error count: 1

我不知道如何修复它,以便将第一个文本框留空不会导致whitelabel错误页面。 如果我将第二个文本字段留空但填写第一个,它的行为完全正常,所以我不确定为什么它会导致错误,如果我反过来尝试它。 任何帮助解决这个问题将不胜感激。

另外,你可能已经注意到我必须在提名和提交者中使用'name'和'sname'作为我的变量,如果我将它们都设置为'name',那么它就无法正常工作。 如果有任何方法可以编辑它以便它们都可以使用“名称”,我很想知道如何。

编辑:找到解决方案。 在WebController中,checkPersonInfo需要为每个要验证的对象单独的BindingResult。 BindingResult需要紧跟在每个@Valid对象之后的方法参数中。

所以,在WebController.java中,这个:

@RequestMapping(value="/", method=RequestMethod.POST)
public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee,
                              @ModelAttribute(value="submitter") @Valid Submitter submitter,
                              BindingResult bindingResult, @Valid Model model) {
    LOG.info("Nominee to string: " + nominee.toString());
    LOG.info("Submitter to string: " + submitter.toString());
    LOG.info("bindingResult to string: " + bindingResult.toString());
    if (bindingResult.hasErrors()) {
        return "form";
    }

    return "redirect:/success";
}

需要变成这样:

@RequestMapping(value="/", method=RequestMethod.POST)
public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee,
                              BindingResult bindingResultNominee,
                              @ModelAttribute(value="submitter") @Valid Submitter submitter,
                              BindingResult bindingResultSubmitter) {
    LOG.info("Nominee to string: " + nominee.toString());
    LOG.info("Submitter to string: " + submitter.toString());
    if (bindingResultNominee.hasErrors() || bindingResultSubmitter.hasErrors()) {
        return "form";
    }

    return "redirect:/success";
}

(模型对象已被删除,因为它从未在任何地方实际使用,如果您需要使用@Valid验证它,那么您将添加第三个BindingResult对象。)

找到了解决方案。 在WebController中,checkPersonInfo需要为每个要验证的对象单独的BindingResult。 BindingResult需要紧跟在每个@Valid对象之后的方法参数中。

所以,在WebController.java中,这个:

    @RequestMapping(value="/", method=RequestMethod.POST)
public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee,
                              @ModelAttribute(value="submitter") @Valid Submitter submitter,
                              BindingResult bindingResult, @Valid Model model) {
    LOG.info("Nominee to string: " + nominee.toString());
    LOG.info("Submitter to string: " + submitter.toString());
    LOG.info("bindingResult to string: " + bindingResult.toString());
    if (bindingResult.hasErrors()) {
        return "form";
    }

    return "redirect:/success";
}

需要变成这样:

    @RequestMapping(value="/", method=RequestMethod.POST)
public String checkPersonInfo(@ModelAttribute(value="nominee") @Valid Nominee nominee,
                              BindingResult bindingResultNominee,
                              @ModelAttribute(value="submitter") @Valid Submitter submitter,
                              BindingResult bindingResultSubmitter) {
    LOG.info("Nominee to string: " + nominee.toString());
    LOG.info("Submitter to string: " + submitter.toString());
    if (bindingResultNominee.hasErrors() || bindingResultSubmitter.hasErrors()) {
        return "form";
    }

    return "redirect:/success";
}

(模型对象已被删除,因为它从未在任何地方实际使用,如果您需要使用@Valid验证它,那么您将添加第三个BindingResult对象。)

通常的情况是在这种情况下使用Dto对象。 这意味着您创建一个包含所有相关表单字段的对象,并根据该字段进行验证。

例如:

@Data
public class MyDto {

    private Nominee nominee;

    private Submitter submitter;
}

@RequestMapping(value="/", method=RequestMethod.POST)
public String checkPersonInfo(@Valid MyDto dto, BindingResult bindingResult) {
    LOG.info("Nominee to string: " + dto.getNominee().toString());
    LOG.info("Submitter to string: " + dto.getSubmitter().toString());
    LOG.info("bindingResult to string: " + bindingResult.toString());
    if (bindingResult.hasErrors()) {
        return "form";
    }
    return "redirect:/success";
}

此外,您可以向控制器添加类似的内容。

@ExceptionHandler(Throwable.class)
public ModelAndView processError(Throwable ex) {
    ex.toString();
    // do something and return a ModelAndView object as you like.
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM