Spring MVC form:errors not showing up - spring

Apologies if this question has been asked before. I'm hoping that someone can step in and help me figure out why my form validation errors aren't showing up.
I'm using Spring 3.0.3 and Hibernate, and I'm using jsr-303 validation to validate my form inputs. I have a Spring controller that handles GETting a page that contains a form that is created with the help of Spring's form taglib. In this form a user is able to change their name and have it saved to the database. If any of the input is empty then the page with the form should be displayed again with error messages. The same controller handles the page's submission. It seems that the controller is functioning properly in most respects, but when there is an error in the user submitted form, no errors are showing up on the page.
Here is what form looks like:
<form:form commandName="changeNameCommand">
<form:errors path="*" cssClass="errorBox" />
<table width="100%" border="0" cellspacing="5" cellpadding="5" align="left">
<tr>
<td><b>First Name:</b></td>
<td><form:input path="firstName" value="${user.firstName}" /></td>
</tr>
<tr>
<td><b>Last Name:</b></td>
<td> <form:input path="lastName" value="${user.lastName}" /></td>
</tr>
</table>
</form:form>
Note that there is a user object in the view that is used to populate the form with the user's current first and last name. This is working properly.
The controller looks something like this:
#Controller
#RequestMapping(value = "/account/settings/change-name")
#SessionAttributes("changeNameCommand")
public class ChangeNameController {
#ModelAttribute("changeNameCommand")
public ChangeNameCommand getCommand() {
return new ChangeNameCommand();
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView getChangeNamePage(HttpServletRequest req) {
ModelAndView mav = new ModelAndView("Account.ChangeName");
mav.addObject("page_title", "Change Name");
return mav;
}
#RequestMapping(method = RequestMethod.POST)
public String doChangeName(
#ModelAttribute("changeNameCommand")
#Valid ChangeNameCommand command,
BindingResult result, SessionStatus status) {
if (result.hasErrors()) {
return "redirect:/account/settings/change-name";
}
// Code here to persist updated user first and last name to database...
status.setComplete();
return "redirect:/home";
}
}
I'm using Tiles 2.1.2 to compose pages and Urlrewrite 3.1.0 to help form friendly urls.
The ChangeNameCommand class looks something like this:
import org.hibernate.validator.constraints.NotEmpty;
public class ChangeNameCommand {
#NotEmpty(message = "You must provide a first name.")
private String firstName;
#NotEmpty(message = "You must provide a last name.")
private String lastName;
#NotEmpty(message = "Your password is required to make changes.")
private String currentPassword;
// Getters and setters here...
}
When debugging, I see that when there is not input for either the first or last name the BindingResult instance does contain errors. My concern is the redirect when there is an error. I've seen other questions here that just return the view name instead of using the redirect: prefix. I tried that but (I think) because of the way I'm using Urlrewrite and the way my servlet mapping is set up Spring returns an error. I've tried returning
/account/settings/change-name
/web/account/settings/change-name
/mywebsite/web/account/settings/change-name
but to no avail. FWIW, here is my servlet-mapping:
<servlet-mapping>
<servlet-name>mywebsite</servlet-name>
<url-pattern>/web/*</url-pattern>
</servlet-mapping>
Any help much appreciated!

using redirect makes all request attributes (including errors and the whole model) disappear. So, don't use redirect, or use the session to temporarily store the data, or use conversations. Or figure out how to use something like a flash-scope (I'm about to in a while)
Another thing - using UrlRewriteFilter with spring-mvc is uncalled for. You have full control over your beatuful REST-like URLs with spring-mvc only.

Here is how I solved my problem. To start off with, I didn't want to drop my use of UrlRewriteFilter and Tiles. However, the problem with this was that, in the case of errors, I couldn't just return the path, as indicated in the controllers RequestMapping annotation. Below is my solution, with the redirect removed in the case of errors, in doChangeName().
#Controller
#RequestMapping(value = "/account/settings/change-name")
#SessionAttributes("changeNameCommand")
public class ChangeNameController {
#ModelAttribute("changeNameCommand")
public ChangeNameCommand getCommand() {
return new ChangeNameCommand();
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView getChangeNamePage() {
ModelAndView mav = new ModelAndView("Account.ChangeName");
mav.addObject("page_title", "Change Name");
return mav;
}
#RequestMapping(method = RequestMethod.POST)
public ModelAndView doChangeName(#ModelAttribute("changeNameCommand") #Valid ChangeNameCommand command,
BindingResult result, SessionStatus status) {
if (result.hasErrors
ModelAndView mav = new ModelAndView("Account.ChangeName");
mav.addObject("page_title", "Change Name");
return mav;
}
// Code here to persist updated user first and last name to database...
status.setComplete();
RedirectView view = new RedirectView("/home");
return new ModelAndView(view);
}
}
Thanks to everyone who helped me out on this!

Related

Replace only selected fields using binding

I'm building simple twitter clone in Spring MVC. I want to provide edit functionality to posted messages.
Message domain object looks like this (simplified)
public class Message {
long id;
String text;
Date date;
User user;
}
I created jps form
<form:form action="edit" method="post" modelAttribute="message">
<table>
<tr>
<td><label for="text">Message: </label></td>
<td><form:textarea path="text" id="text"/></td>
</tr>
<tr>
<td><input type="submit" name="commit" value="Save" /></td>
</tr>
</table>
</form:form>
and added those method in controller class
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public String showEditMessage(#RequestParam long id, Model model) {
Message message = messageService.findMessage(id);
if (message == null) {
return "404";
}
model.addAttribute("message", message);
return "users/editMessage";
}
#RequestMapping(value = "/edit", method = RequestMethod.POST)
public String editMessage(#Valid #ModelAttribute Message message, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "/users/editMessage";
}
messageService.updateMessage(message);
return "/users/editMessage";
}
The problem is that the Message received in editMessage() contains only text field. I assume that this is expected behaviour. Can it be configured to replace fields that are only in jsp form?
I know this is only one field and I could just use #RequestParam String message, but sooner or later I will face similar problem with more than just one field.
I also have side question.
Are attributes added in showEditMessage() are passed to editMessage() method? I tried to add "id" attribute in first method, but I couldn't retrive it using "#RequestParam long id" in second.
#SessionAttributes("message")
On top of controller class solved it.

Spring: #ModelAttribute VS #RequestBody

Please correct me if I am wrong.
Both can be used for Data Binding.
The question is when to use #ModelAttribute?
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(#ModelAttribute Pet pet) { }
In addition, when to use #RequestBody?
#RequestMapping(value = "/user/savecontact", method = RequestMethod.POST
public String saveContact(#RequestBody Contact contact){ }
According to my understanding both serves the similar purpose.
Thanks!!
The simplest way for my understanding is, the #ModelAttribute will take a query string. so, all the data are being pass to the server through the url.
As for #RequestBody, all the data will be pass to the server through a full JSON body.
#ModelAttribute is used for binding data from request param (in key value pairs),
but #RequestBody is used for binding data from whole body of the request like POST,PUT.. request types which contains other format like json, xml.
If you want to do file upload, you have to use #ModelAttribute. With #RequestBody, it's not possible. Sample code
#RestController
#RequestMapping(ProductController.BASE_URL)
public class ProductController {
public static final String BASE_URL = "/api/v1/products";
private ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public ProductDTO createProduct(#Valid #ModelAttribute ProductInput productInput) {
return productService.createProduct(productInput);
}
}
ProductInput class
#Data
public class ProductInput {
#NotEmpty(message = "Please provide a name")
#Size(min = 2, max = 250, message = "Product name should be minimum 2 character and maximum 250 character")
private String name;
#NotEmpty(message = "Please provide a product description")
#Size(min = 2, max = 5000, message = "Product description should be minimum 2 character and maximum 5000 character")
private String details;
#Min(value = 0, message = "Price should not be negative")
private float price;
#Size(min = 1, max = 10, message = "Product should have minimum 1 image and maximum 10 images")
private Set<MultipartFile> images;
}
I find that #RequestBody (also annotating a class as #RestController) is better for AJAX requests where you have complete control over the contents of the request being issued and the contents are sent as either XML or JSON (because of Jackson). This allows the contents to easily create a model object. Conversely, #ModelAttribute seems to be better suited to forms where there is a "command" object backing a form (which may not necessarily be a model object).
You can directly access your "pet" object in view layer, if you use ModelAttribute annotation. Also, you can instantiate this object in a method on your controller to put your model. see this.
ModelAttribute gives you a chance to use this object partial, but with RequestBody, you get all body of request.
I think #ModelAttribute and #RequestBody both are having same use, only difference
is #ModelAttribute use for normal spring MVC and #RequestBody use for REST web service. It is #PathVariable and #PathParam. But in in both the cases we can mix it. we can use #PathVariable in REST and vice versa.
With #ModelAttribute, you pass data in URL params and with #RequestBody you pass it as JSON body. If you're making a REST API then it's better to use #RequestBody. Over most youtube tutorials you might find use of #ModelAttribute - That's simply because they might be demonstrating concepts regarding Spring MVC and are using URL's to pass data.
We need to have the following jsp tag to data bind your entity to the jsp form fields:
The form is from the spring tag library:
The following is the not the full html, but I hope you can relate your self:
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<form:form action="save" method="post" modelAttribute="patient">
<table>
<tr>
<td>Name</td>
<td>
<form:input path="patient.patient_name" /> <br />
</td>
</tr>
<tr>
<td>Phone</td>
<td>
<form:input path="patient.phone_number" /> <br />
</td>
</tr>
<tr>
<td colspan="2"><button type="submit">Submit</button></td>
</tr>
</table>
</form:form>
The form has to be processed twice , once before rendering the form, during which we need to give the appropriate bean instantiation for the property value modelAttribute="patient".
For this the controller class(at the class defintion level) you need to have #RequestMapping annotation.
You need to have the handler method parameters as follows
#GetMapping("logincreate")
public String handleLoginCreate(#ModelAttribute("login") Login login, Model model)
{
System.out.println(" Inside handleLoginCreate ");
model.addAttribute("login",login);
return "logincreate";
}
Spring will scan all handler methods #ModelAttribute and instantiate it with default constructor of Login class, and call all of its getters and setters (for the jsp binding from form to the "login"). In case of missing any of the following the jsp will not be shown, various exceptions are thrown
getters/setters
default constructor
model.addAttribute("login",login);
class level #RequestMapping
method parameter level #ModelAttribute
Also, the handler method of action in the jsp, the in the above form action="save", also the handler method might look like this:
#PostMapping("save")
public String saveLoginDetails(#ModelAttribute("login") Login login, Model model) {
//write codee to insert record into DB
System.out.println(" Inside save login details ");
System.out.println("The login object is " + login.toString());
System.out.println("The model object contains the login attribute"+ model.getAttribute("login"));
loginService.saveLogin(login);
return "welcome";
}
Important learning is:
Before form is launched, spring should have appropriate annotation to indicate the backing bean of the form, in the above example the "backing bean" or "binding object" is Login login with appropriate handler method's parameter annotation #ModelAttribute("login") Login login

Spring redirect keeping parameters after POST

I am using spring 3.2.4 and I have a simple controller that takes a parameter, set it, and redirects the page. For some reason, it is keeping the param even after redirecting. i.e. The page starts at "/" and ends with "/?globalMessage=hereforever", I have tried clearing the modelMap, but that didn't work. I may be misunderstanding something along the lines. I am also adding the model to postHandle in HandlerInterceptor.
#RequestMapping(value = "globalMessage", method = RequestMethod.POST)
public String setGlobalMessage(#RequestParam String globalMessage) {
globalProperties.setProperty("globalMessage", globalMessage);
return "redirect:/";
}
Here is the jsp code in the front
<form method="post" action="/globalMessage">
<input name="globalMessage" type="text" name="message"/>
<input id="submitbutton" type="submit"/>
</form>
Spring's redirect view will automatically expose model attributes as URL parameters: https://jira.springsource.org/browse/SPR-1294. If the globalMessage is set in the model somewhere, then this will get appended to the redirect URL.
You can tell Spring not to do this using the setExposeModelAttributes method.
#RequestMapping(value = "globalMessage", method = RequestMethod.POST)
public String setGlobalMessage(#RequestParam String globalMessage) {
globalProperties.setProperty("globalMessage", globalMessage);
RedirectView redirect = new RedirectView("/");
redirect.setExposeModelAttributes(false);
return redirect;
}

Spring MVC instantiates a new Bean instead of reusing the old one in the Model (#ModelAttribute)

I think my problem is rather simple, but it's been 2 days and I can't figure it out:
I am new to Spring MVC and I am trying to implement a simple #Controller that handles a form.
GET request: I add a new PortfolioBean attribute to the Model.
POST request: I expect to receive a #ModelAttribute with the same PortfolioBean.
#Controller
public class FormController {
#RequestMapping(value = "/form", method = RequestMethod.GET)
public String getForm(Model model) {
PortfolioBean portfolio = new PortfolioBean();
model.addAttribute("portfolio", portfolio);
return "index";
}
#RequestMapping(value = "/form", method = RequestMethod.POST)
public String postForm(#ModelAttribute("portfolio") PortfolioBean portfolio) {
System.out.println("Received portfolio: " + portfolio.getId());
return "showMessage";
}
}
Here is my JSP view:
...
<form:form action="form" commandName="portfolio" method="post">
Name : <form:input path="name" />
Nick Name : <form:input path="nickName" />
Age : <form:input path="age" />
Mobile : <form:input path="mobNum" />
<input type="submit" />
</form:form>
And here is my PortfolioBean:
public class PortfolioBean {
private String name;
private String nickName;
private int age;
private String mobNum;
private static int count = 0;
private int id;
public PortfolioBean() {
count++;
id = count;
System.out.println("NEW BEAN: " + id);
}
// setters & getters
}
As you can see, I added a static count variable to assign incremental IDs, and a println("NEW BEAN!") on the constructor.
My problem is that when I POST the form, I don't receive my original Bean object, instead Spring instantiates a new one, but I want my old Bean :(
Log:
GET /form
NEW BEAN: 1
POST /form
NEW BEAN: 2
Received portfolio: 2
Model attribute only exist in the context of one request. Towards the end of handling the request, the DispatcherServlet adds all the attributes to the HttpServletRequest attributes.
In your first request, you add a Model attribute and it becomes available for use in your jsp.
In your second request, because of the #ModelAttribute, Spring will try to create an instance from your request's request parameters. This will be a completely different instance as the previous one no longer exists.
If you want to reference the old object, you need to store it in a context that spans multiple requests. You can use HttpSession attributes for that purpose, either directly or through flash attributes. You might want to look into RedirectAttributes and #SessionAttributes.

send an object via post

I want to make a form in which I can update my entity in my REST application. For example I have a User entity whith username and realname fields.
Do I need in my request method do something like this
#RequestMapping(value = "/admin/user/update", method = RequestMethod.POST)
public String updateHouse(#RequestBody String username, #RequestBody String realname, Model model)
??
I would prefer to make something like this
#RequestMapping(value = "/admin/house/update", method = RequestMethod.POST)
public String updateHouse(#RequestBody User user, Model model)
I mean that I want to send an object not every field. If i`ll have 20 fields in my entity I would have to add 20 params to my method. Thats not to fancy.
I`m using spring form tag
------- UPDATE
thanks for response. below diffrent entity but real case scenario that i`m trying to start
html code
<c:url var="houseUpdateLink" value="/admin/house/update" />
<form:form method="post" commandName="house" action="${houseUpdateLink}">
<form:input path="house.title" />
<input type="submit" value="send" />
</form:form>
controller method
#RequestMapping(value = "/admin/house/update", method = RequestMethod.POST)
public String updateHouse(#RequestBody House house, Model model) {
model.addAttribute("step", 3);
logger.info("test: " + house.getTitle());
return "houseAdmin";
}
and i receive
HTTP Status 415 -
type Status report
message
description The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ().
You don't need #RequestBody here. Spring will automatically bind the form fields to the House class without it, i.e.
#RequestMapping(value = "/admin/house/update", method = RequestMethod.POST)
public String updateHouse(House house, Model model) {
In the Spring form tags, you can probably do user.username, and pass the User object as a param.

Resources