I am having some issues with my form validation.
Controller:
#RequestMapping(value = REGISTER_URL, method = RequestMethod.POST)
public String registerPost(#Valid RegisterForm registerForm,
BindingResult result) {
if (result.hasErrors()) {
return REGISTER_VIEW;
}
System.out.println(registerForm.getPassword());
return LOGIN_VIEW;
}
View:
<form:form action="register" commandName="registerForm" method="post">
<table>
<tr>
<td>Username:</td>
<td><form:input path='username' /></td>
<td><form:errors path="username"/></td>
</tr>
<tr>
<td>Password:</td>
<td><form:password path='password'/></td>
<td><form:errors path="password"/></td>
</tr>
<tr>
<td>Repeat password:</td>
<td><form:password path='repeatedPassword'/></td>
<td><form:errors path="repeatedPassword"/></td>
</tr>
<tr>
<td colspan="2"> </td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit"> <input name="reset" type="reset"></td>
</tr>
</table>
</form:form>
Form:
public class RegisterForm {
#Size(min = 3, max = 15)
private String username;
#Size(min = 5)
private String password;
#Size(min = 5)
private String repeatedPassword;
// getters and setters omitted
}
When i enter empty values (username, password and repeatedPassword) then no errors occurs (i have checked it using debugger). So it looks like no validation is performed. Binding values from view is ok (checked using debugger) Any ideas what might be wrong?
Add following content into your context:
<mvc:annotation-driven />
<context:component-scan base-package="xxx.xxx.xxx" />
In the guide, they use "#SpringBootApplication"
http://spring.io/guides/gs/validating-form-input/
The #SpringBootApplication annotation is equivalent to using #Configuration, #EnableAutoConfiguration and #ComponentScan with their default attributes:
http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-using-springbootapplication-annotation.html
Related
I'm building a blog in Java using Spring and Hibernate. I can't seem to figure out what is going on but I keep running into a Bad Request error when I try to add (save) a post and I can't figure out where I am wrong in my mapping.
Error message:
Controller:
#Controller
#RequestMapping("/blog")
public class IndexController {
#Autowired
private PostService postService;
#RequestMapping("/list")
public String showPage (Model theModel) {
// get posts from DAO
List<Post> thePosts = postService.getAllPosts();
// add the posts to the model
theModel.addAttribute("allPosts", thePosts);
return "allPosts";
}
#GetMapping("/showFormForAdd")
public String showFormForAdd(Model theModel) {
//create model attribute to bind form data
Post thePost = new Post();
theModel.addAttribute("post", thePost);
return "postSuccess";
}
#PostMapping("/savePost")
public String savePost(#ModelAttribute("post") Post thePost) {
// save the post using our service
postService.savePost(thePost);
return "allPosts";
}
Form snippet:
<div class="table" id="container">
<form:form action="savePost" modelAttribute="post"
method="POST">
<table>
<tbody>
<tr>
<td><label>Title:</label></td>
<td><form:input path="title" /></td>
</tr>
<tr>
<td><label>Author:</label></td>
<td><form:input path="author" /></td>
</tr>
<tr>
<td><label>Date:</label></td>
<td><form:input path="date" /></td>
</tr>
<tr>
<td><label>Post:</label></td>
<td><form:input path="post" /></td>
</tr>
<tr>
<td><label></label></td>
<td><input type="submit" value="Save"></td>
</tr>
</tbody>
</table>
</form:form>
<div style="clear: both;"></div>
<p>
Back to Home Page
</p>
</div>
All other pages are working correctly so far, just can't add an actual blog post. Any help is greatly appreciated.
I figured this out and it is similar to another spring issue I had in the past.
I don't think this really follows a lot of conventional function/design theory, but I added some code into the controller and it now works. I can add a post easily.
First thing was, I removed the #ModelAttribute tag from my "savePost" method. Then I added #RequestParam to my method parameters. Added a little bit of logic and now it saves to the database and then appears on the blog. Good stuff.
Code:
#PostMapping("/savePost")
public String savePost(#RequestParam("author") String author,
#RequestParam("title") String title, #RequestParam("date") String date,
#RequestParam("post") String post) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date theDate = sdf.parse(date);
// save the customer using our service
Post thePost = new Post();
thePost.setAuthor(author);
thePost.setDate(theDate);
thePost.setTitle(title);
thePost.setPost(post);
postService.addPost(thePost);
System.out.println(thePost.toString()); //testing
return "success";
}
jsp:
<form:form action="savePost" modelAttribute="post" method="POST">
<table>
<tbody>
<tr>
<td><label>Title:</label></td>
<td><input id="title" type="text" name="title"></td>
</tr>
<tr>
<td><label>Author:</label></td>
<td><input id="author" type="text" name="author"></td>
</tr>
<tr>
<td><label>Date:</label></td>
<td><input id="date" type="text" name="date"></td>
</tr>
<tr>
<td><label>Post:</label></td>
<td><textarea id="post" type="text"
name="post"></textarea></td>
</tr>
<tr>
<td><label></label></td>
<td><input type="submit" value="Save"></td>
</tr>
</tbody>
</table>
</form:form>
I'm stuck with my code, I'm trying to pass the info of the object "Student". My scenario is like this:
Registration Form (fill the details then press submit button go to next page)
From this view the model will be printed out then press the next button again.
This third page will just show the information again.
Q: How can i pass the same object and display it to other views?
My code is like this.
Registration view:
<form action="/HamburgerProject/stuSubmitAdmissionForm.html" method="post">
<table>
<tr>
<td>Name:</td> <td><input type="text" name="studentName"></td></tr>
<tr>
<td>Age:</td> <td><input type="text" name="studentAge"></td></tr>
<tr>
</table>
<input type="submit" value="submit this">
</form>
First Information View:
<form action="/HamburgerProject/stuSubmitAdmissionForm.html" method="post">
<table>
<tr>
<td>Student's Name :</td>
<td>${student.studentName}</td>
</tr>
<tr>
<td>Student's Age :</td>
<td>${student.studentAge}</td>
</tr>
</table>
<input type="submit" value="send"/>
</form>
Second Information View:
<table>
<tr>
<td>Student's Name :</td>
<td>${student.studentName}</td>
</tr>
<tr>
<td>Student's Age :</td>
<td>${student.studentAge}</td>
</tr>
</table>
My Controller:
#RequestMapping(value="/stuAdmissionForm.html", method = RequestMethod.GET)
public ModelAndView getStudentAdmissionForm() {
ModelAndView model = new ModelAndView("AdmissionForm");
return model;
}
#RequestMapping(value="/stuSubmitAdmissionForm.html", method = RequestMethod.POST)
public ModelAndView submitModelAttributeAnnotationAdmissionForm(#ModelAttribute("student") Student student) {
ModelAndView model = new ModelAndView("AdmissionSuccess");
return model;
}
#RequestMapping(value="/stuDisplayForm.html", method = RequestMethod.POST)
public ModelAndView getStudent(Student student) {
ModelAndView model = new ModelAndView("NewForm");
model.addObject(student);
return model;
}
In attempting to re-display the information from second view to third view the object Student is not being passed.
There are no fields to submit in your fist information view. You have to add the values to hidden fileds:
<form action="/HamburgerProject/stuSubmitAdmissionForm.html" method="post">
<table>
<tr>
<td>Student's Name :</td>
<td>${student.studentName}</td>
</tr>
<tr>
<td>Student's Age :</td>
<td>${student.studentAge}</td>
</tr>
</table>
<input type="hidden" name="studentName" value="${student.studentName}">
<input type="hidden" name="studentAge" value="${student.studentAge}">
<input type="submit" value="send"/>
</form>
I am working on a Spring-MVC application, and I want to validate some data. Currently I am able to validate the data with no problems. Only thing is if the data is invalid, I would like to go to another JSP page, which is not happening right now. Instead I get an Apache 400 error, request was sent syntactically incorrect. Can anyone tell me what all is remaining to implement in validation.
Controller :
#RequestMapping(value = "/", method = RequestMethod.GET)
public String listPersons(Model model) {
Person person = personService.getCurrentlyAuthenticatedUser();
if(!(person==null)){
return "redirect:/canvas/list";
} else {
model.addAttribute("person", new Person());
// model.addAttribute("listPersons", this.personService.listPersons());
model.addAttribute("notices",new Notes());
model.addAttribute("canvases",new Canvas());
return "person";
}
}
#RequestMapping(value= "/person/add", method = RequestMethod.POST)
public String addPerson(#Valid Person person,#ModelAttribute("person") Person p,Model model,BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "redirect:/";
}
this.personService.addPerson(p);
return "redirect:/";
}
Entity :
#Entity
#Table(name="person")
public class Person implements UserDetails{
#NotEmpty #Email
#Column(name = "username")
private String username;
#NotEmpty(message = "Please enter password")
#Column(name = "password")
private String password;
#Size(min = 2,max = 30)
#Column(name = "firstname")
private String firstName;
#Size(min = 2,max = 50)
#Column(name = "secretquestion")
private String secretquestion;
#Size(min = 2,max = 500)
#Column(name = "secretanswer")
private String secretanswer;
}
JSP :
<tr>
<td>
<form:label path="firstName">
<spring:message text="FirstName"/>
</form:label>
</td>
<td>
<form:input path="firstName" />
</td>
<td><form:errors path="firstName"/>Please enter Firstname properly</td>
</tr>
<tr>
<td>
<form:label path="username">
<spring:message text="Email"/>
</form:label>
</td>
<td>
<form:input path="username" />
</td>
<td><form:errors path="username"/>Please enter Email properly</td>
</tr>
<tr>
<td>
<form:label path="password">
<spring:message text="Password"/>
</form:label>
</td>
<td>
<form:input path="password" />
</td>
<td><form:errors path="password"/>Please enter password properly</td>
</tr>
<tr>
<td>
<form:label path="secretquestion">
<spring:message text="secretquestion"/>
</form:label>
</td>
<td>
<form:input path="secretquestion" />
</td>
<td><form:errors path="secretquestion"/>Please enter secretquestion properly</td>
</tr>
<tr>
<td>
<form:label path="secretanswer">
<spring:message text="secretanswer"/>
</form:label>
</td>
<td>
<form:input path="secretanswer" />
</td>
<td><form:errors path="secretanswer"/>Please enter secretanswer properly</td>
</tr>
Servlet-context.xml
<beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"/>
Pom.xml
<!-- Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.1.Final</version>
</dependency>
Any pointers would be nice. Mainly I would like to avoid going on Apache 400, but just display what exactly went wrong with the input field.
This is might be because of public String addPerson(#Valid Person person,#ModelAttribute("person") Person p,Model model,BindingResult bindingResult) signature . BindingResult must follow #ModelAttribute as method signature might have more that one model object and Spring will create a separate BindingResult instance for each of them.That is why when the data is invalid spring is not able to bind errors to BindingResult and throws 400 error.
Try changing method signature to public String addPerson(#Valid Person person,#ModelAttribute("person") Person p,BindingResult bindingResult,Model model).
Read more on BindingResult.
I am learning Spring MVC using Spring In Action 3rd Action, I have implemented the basic program which shows the user registration form and once we submit the form, it will be validated using #Valid.
Here is my Spring Controller:
#Controller
#RequestMapping("/spitter")
public class SpitterController {
private final SpitterService spitterService;
#Inject
public SpitterController(SpitterService spitterService) {
this.spitterService = spitterService;
}
#RequestMapping(method = RequestMethod.GET, params = "new")
public String createSpitterProfile(Model model) {
Spittle spittle = new Spittle();
model.addAttribute(spittle);
model.addAttribute(new Spitter());
return "spittles/edit";
}
#RequestMapping(method = RequestMethod.POST)
public String addSpitterFromForm(#Valid Spitter spitter,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "spittles/edit";
}
spitterService.saveSpitter(spitter);
return "redirect:/spitter/" + spitter.getUsername();
}
}
Here is my Spitter class file:
package com.habuma.spitter.domain;
import java.util.List;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
public class Spitter {
private Long id;
#Size(min = 3, max = 20, message = "User name must be between 3 and 20 characters long.")
#Pattern(regexp = "^[a-zA-Z0-9]+$", message = "Username must be alphanumeric with no spaces")
private String username;
#Size(min = 6, max = 20, message = "The password must be atleast 6 characters long.")
private String password;
#Size(min = 3, max = 50, message = "Your full name must be between 3 and 50 characters long.")
private String fullName;
#Pattern(regexp = "[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}", message = "Invalid email address.")
private String email;
private List<Spittle> spittles;
private boolean updateByEmail;
......Setters & Getters.....
#Override
public boolean equals(Object obj) {
Spitter other = (Spitter) obj;
return other.fullName.equals(fullName)
&& other.username.equals(username)
&& other.password.equals(password);
}
#Override
public int hashCode() {
return super.hashCode();
}
}
This is my edit.jsp file which is shown to the user for registration:
<%# taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>
<div>
<h2>Create a free Spitter account</h2>
<sf:form method="POST" modelAttribute="spitter" enctype="multipart/form-data">
<fieldset>
<table cellspacing="0">
<tr>
<th><label for="user_full_name">Fullname:</label></th>
<td><sf:input path="fullName" size="15" id="user_full_name" />
<sf:errors path="fullName" cssClass="error" /></td>
</tr>
<tr>
<th><label for="user_screen_name">Username:</label></th>
<td><sf:input path="username" size="15" maxlength="15"
id="user_screen_name" /> <small id="username_msg">No spaces,please.</small>
<sf:errors path="username" cssClass="error" /></td>
</tr>
<tr>
<th><label for="user_password">Password:</label></th>
<td><sf:password path="password" size="30" showPassword="true"
id="user_password" /> <small>6
characters or more (betricky!)</small> <sf:errors path="password"
cssClass="error" /></td>
</tr>
<tr>
<th><label for="user_email">EmailAddress:</label></th>
<td><sf:input path="email" size="30" id="user_email" /> <small>In
case you forget something</small> <sf:errors path="email"
cssClass="error" /></td>
</tr>
<tr>
<th></th>
<td><sf:checkbox path="updateByEmail"
id="user_send_email_newsletter" /> <label
for="user_send_email_newsletter">Send me email updates!</label></td>
</tr>
<tr>
<th><label for="image">Profile image:</label></th>
<td><input name="image" type="file" />
</tr>
<tr>
<th></th>
<td><input name="commit" type="submit"
value="I accept.Createmyaccount." /></td>
</tr>
</table>
</fieldset>
</sf:form>
</div>
To load the form I am accessing the URL as : http://localhost:8081/SpringInAction3/spitter?new, once the form is loaded I am just submitting the form without entering any details so that I can check if my form is getting validated or not. But I am getting below exception:
java.lang.NullPointerException
com.habuma.spitter.domain.Spitter.equals(Spitter.java:87)
org.hibernate.validator.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.equals(SingleThreadCachedTraversableResolver.java:138)
java.util.HashMap.get(HashMap.java:305)
org.hibernate.validator.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:45)
org.hibernate.validator.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:757)
org.hibernate.validator.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:324)
org.hibernate.validator.engine.ValidatorImpl.validateConstraintsForRedefinedDefaultGroup(ValidatorImpl.java:273)
org.hibernate.validator.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:256)
org.hibernate.validator.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:210)
org.hibernate.validator.engine.ValidatorImpl.validate(ValidatorImpl.java:119)
org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:106)
org.springframework.validation.DataBinder.validate(DataBinder.java:760)
I am getting NullPointerException in my equals method of Splitter class. Please let me know where I am doing mistake?
Edit:
When I tried to print the values of the fields in my Spitter object I am getting null for all the fields so that is causing the NullPointerException.
This time I removed the equals and hashCode methods from my Spitter class, now when I am submitting the form, the validation is not happening and the page is going to http://localhost:8081/SpringInAction3/spitter/null without showing any errors.
Why the validation is not happening in this case? Also if I just follow the steps in that book, I am getting NullPointerException which is not expected. Please let me know where I am doing mistake?
As per this SO post : #Valid (jsr 303) not working in Spring mvc 3.0, I also have the tag <mvc:annotation-driven/> in my configuration file.
I see one mistake so far. The sf:form attribute enctype is set as multipart/form-data but that is only used on file uploads, so i guess spring mvc is using the MultipartResolver instead of Data binding mechanism that binds form data to form backing objects , try changing it to application/x-www-form-urlencoded, which is the default and correct type for your case.
How to remove some of the "magic value" impression of "command" modelName parameter to create a ModelAndView ?
Example:
#RequestMapping(value = "/page", method = GET)
public ModelAndView render() {
return new ModelAndView("page", "command", new MyObject());
}
One hope was to use a spring constant such as
new ModelAndView("page", DEFAULT_COMMAND_NAME, new MyObject());
I found "command" in the 3 following classes of the spring-webmvc-3.0.5 sources jar:
$ ack-grep 'public.*"command"'
org/springframework/web/servlet/mvc/BaseCommandController.java
140: public static final String DEFAULT_COMMAND_NAME = "command";
org/springframework/web/servlet/mvc/multiaction/MultiActionController.java
137: public static final String DEFAULT_COMMAND_NAME = "command";
org/springframework/web/servlet/tags/form/FormTag.java
56: public static final String DEFAULT_COMMAND_NAME = "command";
The problem is :
BaseCommandController is deprecated
We don't use MultiActionController and FormTag
When you use on your jsp spring tag <form:form>
<form:form method="POST" action="../App/addCar">
<table>
<tr>
<td><form:label path="brand">Name</form:label></td>
<td><form:input path="brand" /></td>
</tr>
<tr>
<td><form:label path="year">Age</form:label></td>
<td><form:input path="year" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
</form:form>
you must write:
#RequestMapping(value = "/car", method = RequestMethod.GET)
public ModelAndView car() {
return new ModelAndView("car", "command", new Car());
}
Because the spring framework expects an object with name "command".
Default command name used for binding command objects: "command".
This name to use when binding the instantiated command class to the request.
http://static.springsource.org/spring/docs/1.2.9/api/org/springframework/web/servlet/mvc/BaseCommandController.html
But when you use html form <form> you can write:
#RequestMapping(value = "/car", method = RequestMethod.GET)
public ModelAndView car() {
return new ModelAndView("car", "YOUR_MODEL_NAME", new Car());
}
But on your page
<form method="POST" action="../App/addCar">
<table>
<tr>
<td><form:label path="YOUR_MODEL_NAME.brand">Name</form:label></td>
<td><form:input path="YOUR_MODEL_NAME.brand" /></td>
</tr>
<tr>
<td><form:label path="YOUR_MODEL_NAME.year">Age</form:label></td>
<td><form:input path="YOUR_MODEL_NAME.year" /></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Submit" />
</td>
</tr>
</table>
</form>
I wouldn't use the default name. If the object is a User call it user, if it's Item call it item. If you need a default (for example - for a generic framework), define your own constant.