I am trying to show validation errors in my form, but I am not able to implement it.
I am trying to pass null values, I get the hibernate validation message but I am not able to see form validation?
Here is my code:
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String saveForm(#Valid Users users,Model model,Errors errors) {
if(errors.hasErrors()) {
return "registerPage";
}
else {
model.addAttribute("message","Registered................");
System.out.println("Save Users TEST------------------------------------>");
userRepository.save(users);
return "register-success";
}
}
Here is the exception:
Field error in object 'users' on field 'salary': rejected value
[null]; codes
[NotNull.users.salary,NotNull.salary,NotNull.java.lang.Integer,NotNull];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [users.salary,salary]; arguments []; default message [salary]];
default message [must not be null] Field error in object 'users' on
field 'dept': rejected value []; codes
[Size.users.dept,Size.dept,Size.java.lang.String,Size]; arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [users.dept,dept]; arguments []; default message [dept],10,2];
default message [Length should be in between 2 to 10] Field error in
object 'users' on field 'name': rejected value []; codes
[Size.users.name,Size.name,Size.java.lang.String,Size]; arguments
[org.springframework.context.support.DefaultMessageSourceResolvable:
codes [users.name,name]; arguments []; default message [name],30,2];
default message [Length should be in between 2 to 30]]
Here is my User Pojo:
public class Users implements Serializable{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Integer id;
#Column(name="name")
#NotNull
#Size(min=2,max=30,message="Length should be in between 2 to 30")
private String name;
#Column(name="salary")
#NotNull
private Integer salary;
#Column(name="dept")
#NotNull
#Size(min=2,max=10,message="Length should be in between 2 to 10")
private String dept;
....
}
Here is my html form(Using thymeleaf):
<h1 th:text="${message}" align="center"></h1>
<div class="container">
<h2>Registration form</h2>
<form class="form-horizontal" th:object="${registerCommand}" th:action="#{/rest/users/register}" method="post">
<div class="form-group">
<label for="name">Username:</label>
<input type="text"
class="form-control" id="name" placeholder="Enter Name"
name="name" th:field="*{name}"> <br />
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}" th:field="*{name}"
class="alert alert-danger"></p>
</div>
<div class="form-group">
<label for="dept">Department:</label>
<input type="text"
class="form-control" id="dept" placeholder="Enter Department"
name="dept" th:field="*{dept}"> <br />
<p th:if="${#fields.hasErrors('dept')}" th:errors="*{dept}" th:field="*{dept}"
class="alert alert-danger"></p>
</div>
<div class="form-group">
<label for="salary">Salary:</label> <input type="text"
class="form-control" id="salary" placeholder="Enter salary"
name="salary" th:field="*{salary}"> <br />
<p th:if="${#fields.hasErrors('salary')}" th:errors="*{salary}" th:field="*{salary}"
class="alert alert-danger"></p>
</div>
<button type="submit" class="btn btn-sucess">Submit</button>
</form>
</div>
Found my reason , i was not passing the correct parameters in my saveForm Controller-
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String saveForm(#ModelAttribute("registerCommand") #Valid Users users, BindingResult bindingResult, Model model){
if(bindingResult.hasErrors()) {
return "registerPage";
}
else {
userRepository.save(users);
return "register-success";
}
}
Note-If you are using BindingResult,it should be exactly after the bean.
It is only BindingResult where the order matters. It must follow the bean that is being validated so that Spring knows where to put any validation errors.
Thanks to David Lavender's answer
https://stackoverflow.com/a/29075342/6547517
Field 'salary' on object User may be null
Check what data you are passing in user object while saving it, there might be salary value is going as null. As you have used #valid on Users , it will not take null or empty value in the input request data
Related
I am getting started with Springboot and am unable to propagate validation results (errors) back to the thyme template. I have a typical setup: #Entity object, #Service, and #Repository. Here are the sections of my Controller and index template (and its form). UserVital, UserBlood, etc. are the data objects mapped to the DB tables using hibernate. Hope this information is enough for the members to point me in the right direction.
Data Object
#Entity
#Table(name = ".....")
public class UserVital {
#NotNull(message = "Height (Feet) cannot be null")
#Range(min = 0, max = 15, message = "Height (Feet) must be greater than 0")
#Column(name = "feet", nullable = false)
private int heightInFeet;
.............
}
Controller
#GetMapping("/")
public String getUsers(Model model) {
UserVital vital = new UserVital();
UserGrithMeasurements grith = new UserGrithMeasurements();
UserBloodChemistry blood = new UserBloodChemistry();
List<Category> categories = categoryService.getAllCategories();
model.addAttribute("categories", categories.get(0));
model.addAttribute("vital", vital);
model.addAttribute("grith", grith);
model.addAttribute("blood", blood);
return "index";
}
#PostMapping("/add")
public String addData(#Valid UserVital vital, BindingResult vitalValidationResult,
#Valid UserGrithMeasurements grith, BindingResult grithValidationResult, #Valid UserBloodChemistry blood,
BindingResult bloodValidationResult, Model model) {
if (vitalValidationResult.hasErrors() || grithValidationResult.hasErrors()
|| bloodValidationResult.hasErrors()) {
return "index";
} else {
model.addAttribute("successMsg", "Details saved successfully!!");
return "index";
}
}
Thyme Form
<form class="tab-content" method="POST" th:action="#{/add}">
<div class="form-group row">
<label for="height" class="col-sm-2 control-label" th:text="#{height}"></label>
<div class="col-sm-2">
<input type="number" class="form-control" id="feet" th:attr="placeholder=#{feet}"
th:field="${vital.heightInFeet}">
</div>
<div class="form-group col-sm-12">
<label for="neck" class="col-sm-2 control-label" th:text="#{neck}"></label>
<div class="col-sm-2">
<input type="number" class="form-control" id="systolic" th:attr="placeholder=#{inches}"
th:field="${grith.neck}">
<div class="col-sm-2">
<input type="number" class="form-control" id="ldl" th:field="${blood.ldl}">
.....
</form>
Question: As you can see I have multiple BindingResult objects. Each BindingResult object holding the validation results of the respective data object (vitalValidationResult holds validation result of UserVital object vital, etc.). Now, how do I write "th:if" statements in the template that will allow me to check if there are any errors in the fields.
Thanks.
I have solved the problem by encapsulating the required form fields in a div and then placing "th:object=${objectName}" in the div.
<form >
<div class="tab-pane active text-center" id="tab_1_1" th:object=${vital}>
<div class="tab-pane active text-center" id="tab_1_2" th:object=${grith}>
</form>
I am getting below error when I am hitting '/signup' API after '/index' API. I have gone through many links. I found mentioned in various resources that the issue is the order of #Valid and BindingResult param needs to be maintained and I see it already in my code still I am unable to find what exactly the problem is. Please help me resolve this issue:
2021-05-31 19:37:40.721 WARN 10224 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 5 errors
Field error in object 'journeyFoodOrder' on field 'contactNoOfGuide': rejected value [null]; codes [NotNull.journeyFoodOrder.contactNoOfGuide,NotNull.contactNoOfGuide,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [journeyFoodOrder.contactNoOfGuide,contactNoOfGuide]; arguments []; default message [contactNoOfGuide]]; default message [Mobile No Of Guide is mandatory]
Field error in object 'journeyFoodOrder' on field 'nameOfCenter': rejected value [null]; codes [NotBlank.journeyFoodOrder.nameOfCenter,NotBlank.nameOfCenter,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [journeyFoodOrder.nameOfCenter,nameOfCenter]; arguments []; default message [nameOfCenter]]; default message [Name Of Center is mandatory]
Field error in object 'journeyFoodOrder' on field 'mealRetrievalTime': rejected value [null]; codes [NotNull.journeyFoodOrder.mealRetrievalTime,NotNull.mealRetrievalTime,NotNull.java.util.Date,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [journeyFoodOrder.mealRetrievalTime,mealRetrievalTime]; arguments []; default message [mealRetrievalTime]]; default message [Meal retrieval time is mandatory]
Field error in object 'journeyFoodOrder' on field 'nameOfGuide': rejected value [null]; codes [NotBlank.journeyFoodOrder.nameOfGuide,NotBlank.nameOfGuide,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [journeyFoodOrder.nameOfGuide,nameOfGuide]; arguments []; default message [nameOfGuide]]; default message [Name Of Guide is mandatory]
Field error in object 'journeyFoodOrder' on field 'dateOfDeparture': rejected value [null]; codes [NotNull.journeyFoodOrder.dateOfDeparture,NotNull.dateOfDeparture,NotNull.java.util.Date,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [journeyFoodOrder.dateOfDeparture,dateOfDeparture]; arguments []; default message [dateOfDeparture]]; default message [Departure date is mandatory]]
Controller Code
#Controller
public class JourneyFoodOrderController {
private final JourneyFoodOrderRepository journeyFoodOrderRepository;
#Autowired
public JourneyFoodOrderController(JourneyFoodOrderRepository journeyFoodOrderRepository) {
// TODO Auto-generated constructor stub
this.journeyFoodOrderRepository = journeyFoodOrderRepository;
}
#GetMapping("/signup")
public String showSignUpForm(#Valid JourneyFoodOrder journeyFoodOrder) {
return "add-journeyFoodOrder";
}
#PostMapping("/addJourneyFoodOrder")
//#Valid AccountForm form, BindingResult result, (binding=false) Account account
public String addJourneyFoodOrder(#Valid JourneyFoodOrder journeyFoodOrder, BindingResult result, Model model) {
if (result.hasErrors()) {
return "add-journeyFoodOrder";
}
model.addAttribute("journeyFoodOrder", journeyFoodOrder);
journeyFoodOrderRepository.save(journeyFoodOrder);
return "redirect:/index";
}
#GetMapping("/index")
public String showJourneyFoodOrderList(Model model) {
model.addAttribute("journeyFoodOrders", journeyFoodOrderRepository.findAll());
return "index";
}
}
View Code
<body>
<div class="container">
<div class="row">
<div class="col-md-4 mt-5">
<div id="successMessage"class="alert alert-success" role="alert">
Order saved successfully
</div>
<div id="failureMessage" class="alert alert-danger" role="alert">
Invalid data
</div>
<form action="#" th:action="#{/addJourneyFoodOrder}" th:object="${journeyFoodOrder}"method="post">
<h2 class="mb-5">New Order</h2>
<div class="form-group">
<label for="nameOfCenter">Name of Center</label>
<input type="text" th:field="*{nameOfCenter}" class="form-control" placeholder="Name of Center">
<span th:if="${#fields.hasErrors('nameOfCenter')}" th:errors="*{nameOfCenter}"></span>
</div>
<div class="form-group">
<label for="nameOfGuide">Name of Guide/Teacher</label>
<input type="text" th:field="*{nameOfGuide}" class="form-control" placeholder="Name of Guide">
<span th:if="${#fields.hasErrors('nameOfGuide')}" th:errors="*{nameOfGuide}"></span>
</div>
<div class="form-group">
<label for="headCount">Head count</label>
<input type="number" th:field="*{headCount}" class="form-control" placeholder="Head count">
<span th:if="${#fields.hasErrors('headCount')}" th:errors="*{headCount}"></span>
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
</div>
<div class="form-group">
<label for="dateOfJourney">Mobile No Of Guide</label>
<input type="text" th:field="*{contactNoOfGuide}" class="form-control" placeholder="Mobile No Of Guide">
<span th:if="${#fields.hasErrors('contactNoOfGuide')}" th:errors="*{contactNoOfGuide}"></span>
</div>
<div class="form-group">
<label for="dateOfDeparture">Date of departure</label>
<div class="input-group date" id="datetimepicker2" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker2" th:field="*{dateOfDeparture}" id="date" placeholder="Choose departure date"/>
<span th:if="${#fields.hasErrors('dateOfDeparture')}" th:errors="*{dateOfDeparture}"></span>
<div class="input-group-append" data-target="#datetimepicker2" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar-alt"></i></div>
</div>
</div>
</div>
<div class="form-group">
<label for="date">Meal retrieval time:</label>
<div class="input-group date" id="datetimepicker1" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker1" th:field="*{mealRetrievalTime}" id="date" placeholder="Choose meal retrieval date and time"/>
<span th:if="${#fields.hasErrors('mealRetrievalTime')}" th:errors="*{mealRetrievalTime}"></span>
<div class="input-group-append" data-target="#datetimepicker1" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar-alt"></i></div>
</div>
</div>
</div>
Model
#Entity
public class JourneyFoodOrder{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NotBlank(message="Name Of Center is mandatory")
private String nameOfCenter;
#NotBlank(message="Name Of Guide is mandatory")
private String nameOfGuide;
#NotNull(message="Head Count is mandatory")
private int headCount;
#NotNull(message="Mobile No Of Guide is mandatory")
private String contactNoOfGuide;
#NotNull(message="Departure date is mandatory")
#DateTimeFormat(pattern = "dd/MM/yyyy")
private Date dateOfDeparture;
#NotNull(message="Meal retrieval time is mandatory")
#DateTimeFormat(pattern = "dd/MM/yyyy h:mm a")
private Date mealRetrievalTime;
#NotNull(message="Thepla Count is mandatory")
private int thepla;
#NotNull(message="Puri Count is mandatory")
private int puri;
#NotNull(message="Roti Count is mandatory")
private int roti;
#NotNull(message="Achar count is mandatory")
private int achar;
#NotNull(message="Jam count is mandatory")
private int jam;
#NotNull(message="Bread count is mandatory")
private int bread;
#NotNull(message="Other items count is mandatory")
private int others;
//..............setters and getters
}
The problem is that you have #Valid annotation on the #GetMapping method:
#GetMapping("/signup")
public String showSignUpForm(#Valid JourneyFoodOrder journeyFoodOrder) {
return "add-journeyFoodOrder";
}
This indicates that JourneyFoodOrder already should be valid when showing the form for the first time, which is never true. Use this in stead:
#GetMapping("/signup")
public String showSignUpForm(JourneyFoodOrder journeyFoodOrder) {
return "add-journeyFoodOrder";
}
I personally find it clearer to do this:
#GetMapping("/signup")
public String showSignUpForm(Model model) {
model.addAttribute("journeyFoodOrder", new JourneyFoodOrder());
return "add-journeyFoodOrder";
}
But both will work fine.
Additional notes:
You need to use #ModelAttribute for your form data object parameter in the #PostMapping method:
public String addJourneyFoodOrder(#Valid #ModelAttribute("journeyFoodOrder") JourneyFoodOrder journeyFoodOrder, BindingResult result, Model model) {
...
}
After fixing the #GetMapping method, you will run into another problem because add-journeyFoodOrder.html has this line that should be removed:
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
You might want to use a dedicated form data object to bind to the HTML form instead of using your entity directly. See Form handling with Thymeleaf for more information on that.
I'm trying to validate the form. Spring correctly validates, but when errors should be returned, it receives an exception:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'employee' available as request attribute
Add I don't know what is wrong. Could you help me?
Controller:
#RequestMapping(value = "/employee/add",method = RequestMethod.POST)
public String addEmployee(#Valid DTOEmployee dtoEmployee, BindingResult result) {
if (result.hasErrors()) {
return "employee_add";
}
employeeService.save(dtoEmployee);
return "redirect:/employee?add";
}
DTOEmployee:
public class DTOEmployee{
#NotNull
private String name;
#NotNull
private String subname;
#NotNull
private String email;
}
fragments of employee_add:
<form th:action="#{/employee/add}" th:object="${employee}" method="post" class="form-inline justify-content-center">
<div class="input-group my-1">
<input th:field="*{name}" type="text" id="name" class="form-control"
placeholder="Your name"/>
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name error</p>
<input th:field="*{subname}" type="text" id="name" class="form-control"
placeholder="Your subname"/>
<p th:if="${#fields.hasErrors('subname')}" th:errors="*{name}">Name error</p>
<input th:field="*{email}" type="email" id="email" class="form-control"
placeholder="Your mail"/>
<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Name error</p>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
messages.properties:
NotNull.DTOEmployee.name = Name must be not null
NotNull.DTOEmployee.subname= Subname must be not null
NotNull.DTOEmployee.email= Email must be not null
Seems like you are mixing thymeleaf form submission with requests. As stated in the example from spring.io https://spring.io/guides/gs/handling-form-submission/
#PostMapping("/greeting")
public String greetingSubmit(#ModelAttribute Greeting greeting) {
return "result";
}
Seems you are missing #ModelAttribute
Hmmm... I`m added #ModelAttribute [ public String addEmployee(#Valid #ModelAttribute DTOEmployee dtoEmployee, BindingResult result)] and I get the following exception:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor' (template: "employee_add" - line 44, col 44)] with root cause
line 44 in employee_add is:
<input th:field="*{name}" type="text" id="name" class="form-control"
EDIT:
I forgot to add the attribute name in quotes:
public String addEmployee(#Valid #ModelAttribute("employee") DTOEmployee dtoEmployee, BindingResult result)
It`s work :)
I am trying to create a form with 3 fields within the class LoginForm: usuario, senha, msgLogin.
I receive the fields usuário and senha from the input boxes and then I try to redirect to the same page with the same fields plus the field MsgLogin. But I've not been able to update the msgLogin field and I don't understand why.
These are the code:
HTML:
<form id="frmLogin" class="form col-md-12" action="#" th:action="#{/login}" th:object="${loginForm}" method="post">
<div class="form-group">
<label for="usuario" class="col-md-1">Usuário: </label>
<input type="text" id="usuario" placeholder="Email" th:field="*{usuario}"/>
</div>
<div class="form-group">
<label for="senha" class="col-md-1">Senha: </label>
<input type="password" id="senha" placeholder="senha" th:field="*{senha}"/>
</div>
<div class="row">
<button id="entrar">Entrar</button>
</div>
<div class="row">
<div id="msgLogin"></div>
<p th:text="${loginForm.msgLogin}" />
</div>
</form>
The Controller:
#RequestMapping("/")
public String init(#ModelAttribute LoginForm loginForm) {
Logger.getAnonymousLogger().info("Tela Inicial.");
return "home";
}
#PostMapping("/login")
public ModelAndView entrar(LoginForm loginForm) throws IOException {
Logger.getAnonymousLogger().info("Entrando no Internet Banking.");
service.login(usuario, senha);
ModelMap model = new ModelMap();
loginForm.setMsgLogin("I want to update that value!");
model.addAttribute("loginForm", loginForm);
return new ModelAndView("redirect:/", model);
}
Class using Lombok:
#Getter
#Setter
public class LoginForm {
private String usuario;
private String senha;
private String msgLogin;
}
A redirect send a 302 HTTP status back to the browser to resubmit using a new url, so it resubmits the same two fields to the new url. There's no expectation of payload data in the 302 response.
In your Controller, change the last line in your entrar method to: return new ModelAndView("/login", model);
I am woring on an Spring Web application with the CURD operation for the Entity Department, this is the handler for processing the submit of a new Department:
#RequestMapping(value = "/", method = RequestMethod.POST)
public String addSubmit(#Valid Department department, BindingResult result,SessionStatus status) {
if (result.hasErrors()) {
return "department/add";
} else {
departmentService.save(department);
status.setComplete();
return "redirect:/departments/" + department.getId();
}
}
Model:
#Entity
public class Department extends BaseNamedEntity {
#NotNull
#NotEmpty
private String description;
...
}
And the form:
<form:form method="post" action="${fmURL}" class="form-horizontal" commandName="department">
<form:hidden path="id"/>
<spring:bind path="name">
<div class="control-group ${status.error ? 'error' : ''}">
<label class="control-label">Name</label>
<div class="controls">
<form:input path="name"/>
<span class="help-inline">${status.errorMessage}</span>
</div>
</div>
</spring:bind>
<form:form>
However, once I submit the form with empty field, normally, the errorMessage should be displayed and the related div should be add a class with error, however I can not found the error message.
What's going on?
BTW, the example is derive from Spring-petclinic project.