Reputation: 1986
I am trying to add code-side validation to my form. I am basing on this tutorial: https://www.javacodegeeks.com/2017/10/validation-thymeleaf-spring.html - but without effort.
I have an entity 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<>();
And a Controller method:
@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";
}
}
And a small piece of the Thymeleaf for clearness (all other fields look alike this one)
<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>
The problem is that when I am trying to send an invalid form, I am not redirected to /add
, but I get an error page saying:
There was an unexpected error (type=Bad Request, status=400). Validation failed for object='invoiceData'. Error count: 6
And the stacktrace (from just one field, for clearness):
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]
So I presume that this is one of the behaviours that I can exptect from the validator.
But there is one thing, when I set a breakpoint in the controller, at the beginning of the method where the if
statement begins, AND I send an invalid form, the debugger never stops there, so it seems that this code is never reached...
But when I send a correctly filled form - everything goes fine, the code works, data is sent to the database etc...
My question is: is this a normal behaviour of the validator? What can I do make the code run when form is invalid, so I can get the BindingResult and show some error output to the user?
Upvotes: 0
Views: 1911
Reputation: 94
You need to move the BindingResult parameter right next to parameter having @Valid annotation.
@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";
}
}
Now the BindingResult variable will be attached to InvoiceData variable. Also if you are Validating multiple parameters in a API, you would require to declare its corresponding BindingResult variable right next to all of these.
Upvotes: 2