I am trying to get model validation working with my spring boot mvc application. I am using freemarker as view templates. My problem is now that even though model validation works as expected the model errors are not shown in my view.
Here is some example code. The Model:
public class TestModel {
#Size(min=2, max=10)
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The Controller
#Controller
#RequestMapping("/test")
public class OrderController extends BaseController {
#RequestMapping(value = "new/greenhouse", method = RequestMethod.GET)
public String get(#ModelAttribute TestModel testModel) {
testModel.setName("Hello world");
return "test.ftl";
}
#RequestMapping(value = "/test", method = RequestMethod.POST)
public String post(#ModelAttribute #Valid TestModel testModel, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
// The bindingResult contains the "correct" errors
return "test.ftl";
}
return "redirect:/";
}
}
The view:
<#import "../layout.ftl" as layout />
<#import "/spring.ftl" as spring />
<#import "../freemarker.ftl" as ftl />
<#layout.defaultLayout>
<form action="/test" method="post">
<#ftl.csrfToken />
Name:
<#spring.formInput "testModel.Name" />
<#spring.showErrors "<br>" /> <#-- Does not print the error as status.errorMessages is not populated -->
<#-- This will at least display the default Message -->
<#list spring.status.errors.allErrors as error>
<span class="has-error">${error.defaultMessage} </span>
</#list>
</form>
</#layout.defaultLayout>
Any idea why status.errorMessages does not get populated?
So I finally found the solution to this:
The property name in the Freemarker view has to be lowercase (as is the private member name in the model class). The correct view would look like this:
<#spring.formInput "testModel.name" />
<#spring.showErrors "<br>" />
Related
I try to upload a .csv file's data into database, but when i upload and submit it, it throws nullpointexception. Means, when I print name in controller, name is printed, but when i try to get the file, it show null.
FileUpload model class
public class FileUpload {
private CommonsMultipartFile[] files;
private String name;
// Getters and setters
}
Controller
#RequestMapping(value = "uploadPage", method = RequestMethod.GET)
public ModelAndView uploadPage() {
ModelAndView model = new ModelAndView("upload_page");
FileUpload formUpload = new FileUpload();
model.addObject("formUpload", formUpload);
return model;
}
#RequestMapping(value = "/doUpload", method = RequestMethod.POST)
public String doUpload(#ModelAttribute("formUpload") FileUpload fileUpload, BindingResult result) throws IOException, JAXBException {
System.out.println("myfirl "+fileUpload.getFiles()); // output is null
System.out.println("name "+fileUpload.getName()); // name is displaying
//other stuffs
}
upload_page
<spring:url value="/doUpload" var="doUploadURL"/>
<form:form method="post" modelAttribute="formUpload" action="${doUploadURL }" enctype="multipart/form-data">
<form:input path="files" type="file" multiple="multiple"/>
<form:input path="name" type="text"/>
<button type="submit">Upload</button>
</form:form>
WebConfig
#Bean(name="multipartResolver")
public CommonsMultipartResolver getResolver(){
CommonsMultipartResolver commonsMultipartResolver=new CommonsMultipartResolver();
commonsMultipartResolver.setMaxUploadSizePerFile(20*1024*1024);
return commonsMultipartResolver;
}
I tried to sort it out in many ways, but failed. Anyone try to sort it out? Thanks in advance
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
#ResponseBody
public String uploadFileHandler(#RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
System.out.println(file.getName);
}
}
or you can do that
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public String uploadFileHandler(MultipartHttpServletRequest request) {
Iterator<String> itr = request.getFileNames();
while (itr.hasNext()){
System.out.println(itr.next().toString());
}
....
}
I'm trying to use a type converter in a Spring boot app and using Thymeleaf but I can't get it working. I've put some code on Github so you can see exactly what I'm trying to do. This is Spring 1.5.1 and Thymeleaf 3.0.3. https://github.com/matthewsommer/spring-thymeleaf-simple-converter
Basically this code is just trying to add a person to a comment object. The person object is null when it gets posted and I don't understand why.
Something that's odd is that the ID of the person isn't being added to the value attribute but it is if th:field="*{body}" is removed. I think it has to do with this: https://github.com/thymeleaf/thymeleaf/issues/495 but I'm currently trying to add BindingResult and it's not working...
My HTML is:
<body>
<div th:if="${personObject != null}" th:text="${personObject.name}"></div>
<form th:action="#{/}" th:object="${comment}" method="post">
<input type="hidden" th:if="${personObject != null}" th:value="${personObject.id}" th:field="*{person}" />
<textarea id="comment" placeholder="Comment..." th:field="*{body}"></textarea>
<button id="comment_submit" type="submit">Comment</button>
</form>
<div th:text="${comment.body}"></div>
</body>
My controller:
#Controller
public class HomeWebController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String getHome(final HttpServletRequest request, final Map<String, Object> model, #ModelAttribute(value = "comment") Comment comment) {
model.put("personObject", new Person(1, "John Smith"));
return "Home";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public String postHome(final HttpServletRequest request, final Map<String, Object> model, #ModelAttribute(value = "comment") Comment comment) {
model.put("commentBody", comment.getBody());
model.put("person", comment.getPerson());
return "Home";
}
}
And the converter:
#Component
public class StringToPersonConverter implements Converter<String, Person> {
#Autowired
public StringToPersonConverter() { }
#Override
public Person convert(String id) {
if(id == "1") {
Person person = new Person(1, "John Smith");
return person;
}
return null;
}
}
Hi finally I had to do some changes to make it work, but this is the result class by class.
ConvertorApplication:
#SpringBootApplication
#Configuration
#EnableWebMvc
public class ConvertorApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(ConvertorApplication.class, args);
}
//Add converter and configuration annotation
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToPersonConverter());
}
}
StringToPersonConverter:
#Override
public Person convert(String id) {
//Never compare String with == use equals, the "==" compares memory space not the values
if(id.equals("1")) {
Person person = new Person(1, "John Smith");
return person;
}
return null;
}
HomeWebController
#Controller
public class HomeWebController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String getHome(final Map<String, Object> model, #ModelAttribute(value = "comment") Comment comment) {
//Initialize the comment with the person inside, no need of personObject object
model.put("comment", new Comment(new Person(1, "John Smith")));
return "Home";
}
#RequestMapping(value = "/", method = RequestMethod.POST)
public String postHome(final Map<String, Object> model,
#ModelAttribute(value = "comment") Comment comment,
#RequestParam(value = "person.id") Person person) {
//from the view retrieve the value person.id which will be used by the converter to build the Person entity
comment.setPerson(person);
model.put("comment", comment);
return "Home";
}
}
Comment (Add empty constructor)
public Comment(){}
Person (Add empty constructor)
public Person(){}
Home.jsp (Basically remove personObject, not need)
<!DOCTYPE html>
<html xmlns:th="//www.thymeleaf.org">
<body>
<div th:text="${comment.person.name}"></div>
<form th:action="#{/}" th:object="${comment}" method="post">
<input type="hidden" th:field="*{person.id}" />
<textarea id="comment" placeholder="Comment..." th:field="*{body}"></textarea>
<button id="comment_submit" type="submit">Comment</button>
</form>
<div th:text="${comment.body}"></div>
</body>
</html>
That's would be everything to make it work.
#Controller
#RequestMapping("Page/Login.do")
public class HomeController
{
#RequestMapping(method = RequestMethod.GET)
protected String showLoginPage(HttpServletRequest req,BindingResult result) throws Exception
{
loginuser lu=new loginuser();
lu.setLoginn("Amit");
System.out.println(lu.getLoginn());
return "Login";
}
}
Above code is ##HomeController.java##
loginuser.java
package Com.Site.Name.Order;
public class loginuser
{
private String Loginn;
public String getLoginn()
{
System.out.println("hi i m in login get");
return Loginn;
}
public void setLoginn(String loginn)
{
System.out.println("I m in Loin set");
Loginn = loginn;
}
}
My JSP PAGE IS
Login.jsp
<form:form action="Login.do" method="post" commandName="loginuser">
<div id="Getin">
<img alt="" src="Image/loginttt.png">
</div>
<div id="login">
</div>
<form:input path="Loginn"/>
<input type="submit" value="LOGIN"/>
</form:form>
You are trying to use a Model attribute (commandName) but there is no such attribute added to the model or request attributes. You need to add it. Also, the BindingResult in your handler makes no sense. Remove it.
#RequestMapping(method = RequestMethod.GET)
protected String showLoginPage(HttpServletRequest req, Model model) throws Exception
{
loginuser lu=new loginuser();
lu.setLoginn("Amit");
System.out.println(lu.getLoginn());
model.addAttribute("loginuser", lu);
return "Login";
}
The Model attributes are added to the request attributes and are therefore available in the jsp.
I am trying to add form validations to a working application. I started by adding a NotNull check to Login Form. I am using Hibernate impl of Bean Validation api.
Here's the code I have written
#Controller
#RequestMapping(value="/login")
#Scope("request")
public class LoginController {
#Autowired
private CommonService commonService;
#Autowired
private SiteUser siteUser;
#InitBinder
private void dateBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
CustomDateEditor editor = new CustomDateEditor(dateFormat, true);
binder.registerCustomEditor(Date.class, editor);
}
#ModelAttribute
protected ModelMap setupForm(ModelMap modelMap) {
modelMap.addAttribute("siteUser", siteUser);
return modelMap;
}
#RequestMapping(value="/form", method = RequestMethod.GET)
public ModelAndView form(ModelMap map){
if (siteUser.getId() == null){
map.addAttribute("command",new SiteUser());
return new ModelAndView("login-form",map);
}else {
return new ModelAndView("redirect:/my-dashboard/"+siteUser.getId());
}
}
#RequestMapping(value="/submit", method=RequestMethod.POST)
public ModelAndView submit(#Valid SiteUser user, ModelMap map, BindingResult result){
if (result.hasErrors()) {
map.addAttribute("command", user);
System.out.println("Login Error block");
return new ModelAndView("login/form",map);
}
else {
User loggedInUser = commonService.login(user.getEmail(), user.getPassword());
if (loggedInUser != null) {
siteUser.setId(loggedInUser.getId());
siteUser.setName(loggedInUser.getName());
System.out.println("site user attr set");
}
return new ModelAndView("redirect:/my-dashboard/"+loggedInUser.getId());
}
}
}
The Model is
#Component
#Scope("session")
public class SiteUser {
private Integer id = null;
#NotNull
private String name = null;
private String email = null;
private String password = null;
private List<String> displayPrivList = null;
private List<String> functionPrivList = null;
// And the getters and setters
}
The JSP is
<c:url var="loginSubmitUrl" value="/login/submit"/>
<form:form method="POST" action="${loginSubmitUrl}">
<form:errors path="*" />
<div class="row">
<div class="span4">
</div>
<div class="span4">
<h3>Please Login</h3>
<label><span style="color:red">*</span>Email</Label><form:input path="email" type="text" class="input-medium" />
<label><span style="color:red">*</span>Password</Label><form:input path="password" type="password" class="input-medium" />
<br/>
<button type="submit" class="btn btn-primary">Login</button>
<button type="button" class="btn">Cancel</button>
</div>
</div>
</form:form>
I have added messages.properties and the annotation driven bean def in the context xml.
Other answers on the subject talk about form fields not getting posted. In my case, that's the expected behavior - that if I submit a blank form, I should get an error.
Please advise what am I missing?
I think this question had the same issue as yours
Syntactically incorrect request sent upon submitting form with invalid data in Spring MVC (which uses hibernate Validator)
which just points out
You have to modify the order of your arguments. Put the BindingResult result parameter always directly after the parameter with the #Value annotation
You need this: <form:errors path="email" cssClass="errors" />
Use the tag form:errors for each input with the same "path" name.
It is also possible to list all the error at the same time if you don't put a path.
Here, check an full example with sample code that you can download to learn how to do:
http://www.mkyong.com/spring-mvc/spring-3-mvc-and-jsr303-valid-example/
Can you try changing the <form:form> by including the commandName to it like this
<form:form method="POST" action="${loginSubmitUrl}" commandName="user">
I'm new to Tomcat and Spring Web. I'm trying to use Spring's form validation features by following this tutorial. Everything seems to run smoothly except for one thing... my form doesn't do any validation and I can always get to the success page when I send the form no matter which data I provide.
Am I using the constraints correctly? I want to enforce that the user fills in their first name and that the first name be at least two characters long.
package net.devmanuals.form;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotEmpty;
public class RegistrationForm {
#NotEmpty(message = "You surely have a name, don't you?")
#Size(min = 2, message = "I'm pretty sure that your name consists of more than one letter.")
private String firstName;
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getFirstName() {
return this.firstName;
}
}
Form code:
<form:form method="post" commandName="regform">
<p><form:input path="firstName" /> <form:errors path="firstName" /></p>
<p><input type="submit" /></p>
</form:form>
The controller:
#Controller
#RequestMapping("/register")
public class RegistrationController {
#RequestMapping(method = RequestMethod.GET)
public String showRegForm(Map model) {
RegistrationForm regForm = new RegistrationForm();
model.put("regform", regForm);
return "regform";
}
#RequestMapping(method = RequestMethod.POST)
public String validateForm(#Valid RegistrationForm regForm, BindingResult result, Map model) {
if (result.hasErrors()) {
return "regform";
}
model.put("regform", regForm);
return "regsuccess";
}
}
Am I applying the constraints incorrectly?
In addition to adding <mvc:annotation-driven/> to your config, you need to make sure the JSR-303 jar is on your classpath. From the docs:
[AnnotationDrivenBeanDefinitionParser] ... configures the validator if specified, otherwise defaults to a fresh Validator instance created by the default LocalValidatorFactoryBean if the JSR-303 API is present on the classpath.