java - 在 Spring Boot 应用程序中使用 @Valid 和 BindingResult 时出现表单输入验证问题

标签 java spring spring-boot validation thymeleaf

我正在尝试向我的表单添加代码端验证。我基于本教程:https://www.javacodegeeks.com/2017/10/validation-thymeleaf-spring.html - 但不费力气。

我有一个实体 InvoiceData:

@Data
@Document
@NoArgsConstructor
public class InvoiceData {

    @Id private String id;
    private ContractorData data;
    @NotNull
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date receptionDate;
    @NotNull
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date orderDate;
    @NotNull
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date invoiceIssueDate;
    @NotNull
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @NotNull
    private Date contractDate;
    @NotBlank
    private String invoiceNumber;
    private String additionalCosts;
    private String contractorComment;
    @NotEmpty
    private List<InvoiceTask> invoiceTasks = new ArrayList<>();

还有一个 Controller 方法:

@RequestMapping(value = "/addinvoice/{contractorId}", method = RequestMethod.POST, produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public String addInvoice(@PathVariable("contractorId") String contractorId, @ModelAttribute @Valid InvoiceData data, Model model, BindingResult result, RedirectAttributes attr, HttpSession session) {
        if (result.hasErrors()) {
            System.out.println("BINDING RESULT ERROR");
            attr.addFlashAttribute("org.springframework.validation.BindingResult.data", result);
            attr.addFlashAttribute("register", result);
            return "redirect:/add";
        } else {
            Contractor contractor = contractorRepository.findById(contractorId).get();
            data.setData(contractor.getContractorData());
            if (contractor.getInvoices() == null) {
                contractor.setInvoices(new ArrayList<InvoiceData>());
            }
            contractor.getInvoices().add(data);
            invoiceDataRepository.save(data);
            contractorRepository.save(contractor);
            model.addAttribute("contractor", contractor);
            return "index";
        }
    }

为了清晰起见,还有一小块 Thymeleaf(所有其他字段看起来都与此类似)

<form action="#" th:action="@{addinvoice/{id}(id=${contractorid})}" th:object="${invoicedata}" method="post">
    <ul class="form-style-1">
        <li>
            <label>Reception date<span class="required">*</span></label>
            <input type="date" th:field="*{receptionDate}" id="receptionDate">
        </li>

问题是,当我尝试发送无效表单时,我没有重定向到 /add,但收到一个错误页面:

There was an unexpected error (type=Bad Request, status=400). Validation failed for object='invoiceData'. Error count: 6

以及堆栈跟踪(为了清晰起见,仅来自一个字段):

Field error in object 'invoiceData' on field 'invoiceIssueDate': rejected value [null]; codes [NotNull.invoiceData.invoiceIssueDate,NotNull.invoiceIssueDate,NotNull.java.util.Date,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [invoiceData.invoiceIssueDate,invoiceIssueDate]; arguments []; default message [invoiceIssueDate]]; default message [must not be null]

所以我认为这是我可以预期 validator 的行为之一。

但是有一件事,当我在 Controller 中设置断点时,在 if 语句开始的方法的开头,并且我发送了一个无效的表单,调试器永远不会在那里停止,所以看来这个代码永远不会达到......

但是当我发送正确填写的表单时 - 一切正常,代码正常工作,数据发送到数据库等......

我的问题是:这是 validator 的正常行为吗?当表单无效时,我该怎么做才能运行代码,以便我可以获得 BindingResult 并向用户显示一些错误输出?

最佳答案

您需要将 BindingResult 参数移至具有 @Valid 注释的参数旁边。

@RequestMapping(value = "/addinvoice/{contractorId}", method = RequestMethod.POST, produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String addInvoice(@PathVariable("contractorId") String contractorId, @ModelAttribute @Valid InvoiceData data, BindingResult result, Model model , RedirectAttributes attr, HttpSession session) {
    if (result.hasErrors()) {
        System.out.println("BINDING RESULT ERROR");
        attr.addFlashAttribute("org.springframework.validation.BindingResult.data", result);
        attr.addFlashAttribute("register", result);
        return "redirect:/add";
    } else {
        Contractor contractor = contractorRepository.findById(contractorId).get();
        data.setData(contractor.getContractorData());
        if (contractor.getInvoices() == null) {
            contractor.setInvoices(new ArrayList<InvoiceData>());
        }
        contractor.getInvoices().add(data);
        invoiceDataRepository.save(data);
        contractorRepository.save(contractor);
        model.addAttribute("contractor", contractor);
        return "index";
    }
}

现在 BindingResult 变量将附加到 InvoiceData 变量。此外,如果您要验证 API 中的多个参数,则需要在所有这些参数旁边声明其相应的 BindingResult 变量。

关于java - 在 Spring Boot 应用程序中使用 @Valid 和 BindingResult 时出现表单输入验证问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55723472/

相关文章:

java - Spring Boot 2.0.3 Oauth2 安全 : Getting 401 error even when using access token in header

java - *系列* Bean 的多个实例?

java - 在 spring 中使用 id 保护端点

java - OAuth2 基于角色的身份验证

java - Spring @Valid 无法在 Post Controller 中正常工作(多部分)

java - 需要在数据库中存储字节码

java - 为什么 Toast.makeText(...) 和 toastObj.makeText(...) 运行方式不同?

java - 当我只访问外键 ID 时,如何防止 Hibernate 获取连接的实体?

java - 如何在 Spring 中提高具有更大结果集的查询执行的性能

java - 如何防止 Controller 方法在 Chrome 浏览器的 Spring Boot 中运行两次?