Spring: #ModelAttribute VS #RequestBody - spring

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

Related

In MVC how is the data passed from JSP to Controller?

I'm following the Spring MVC tutorial here: http://www.tutorialspoint.com/spring/spring_mvc_form_handling_example.htm
and I'm not getting the logic how is the data passed from JSP to Controller.
I think I understand how the data is passed from the Controller to the JSP, but after the user has edited the form on the JSP how is the data passed to the Controller?
In the controller:
public String addStudent(#ModelAttribute("SpringWeb")Student student, ModelMap model)
question:
How the controller knows that from the form on the jsp Student class instance student with name, age and id are passed?
I have this example working. I have altered the example to display a list of students, but I am not able to get the list from JSP to Controller:
#RequestMapping(value = "/student", method = RequestMethod.POST)
public ModelAndView studentSave(#ModelAttribute("listOfStudents") ArrayList<Student> listOfStudents,ModelMap model)
{
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
StudentJDBCTemplate studentJDBCTemplate = (StudentJDBCTemplate) context.getBean("studentJDBCTemplate");
System.out.println("Size of listOfStudents is = " + listOfStudents.size());
...
listOfStudents.size() returns 0.
question: what am i missing here, why I can't get the list from the form on the jsp?
question: How the controller knows that from the form on the jsp
Student class instance student with name, age and id are passed?
When you submit the form you are making an HTTP (typically, POST) request to a given URL. This POST request will contain the values in
the form as request parameters. If you were not using any web framework (e.g. Spring MVC) then you would typically work directly with the Servlet API
to extract and work with these values, particularly the HttpServletRequest object.
http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html
You can try this is your application by adding the following (the Spring MVC framework will automatically pass in the request).
public String addStudent(#ModelAttribute Student student, HttpServletRequest request){
for(String key : request.getParameterMap().keySet()){
System.out.println(key + "=" + request.getParameterMap().get(key);
}
}
Now, regardless of the framework you are using the underlying mechanism does not change, the parameters are still sent in the POST request as simple Strings.
The framework however essentially adds an abstraction layer on top of this to prevent you having to write boilerplate to extract and manually work with these
parameters. So, rather than having to do the following:
public String addStudent(HttpServletRequest request){
Student student = new Student();
student.setId(Integer.parseInt(request.getParameter("id"));
student.setName(request.getParameter("name"));
....
}
you let the framework take care of it.
public String addStudent(#ModelAttribute Student student){
}
The #ModelAttribute tells the framework you want the submitted parameters to be bound to a Student instance. On submit, the framework will create a new Student
instance and, by means of reflection, (http://docs.oracle.com/javase/tutorial/reflect/) set the various fields to the corresponding HTTP params.
As for the 2nd part of your question there are numerous examples of how to bind to a Collection. See below for example:
http://viralpatel.net/blogs/spring-mvc-multi-row-submit-java-list/
Generally 'work' is done in the controller, and the results are passed to the JSP simply for display. The JSP renders to the user's browser over HTTP and then the user modifies the page and posts back to the controller.
If you're doing 'work' in the JSP that needs to be passed back to the controller before the page is sent to the user, then you should consider finding a way of doing all that in the controller.
Having said this. The model that you pass to the JSP doesn't have to contain simple objects. You can pass to the JSP an object that has methods on it that performs processing. Then in the JSP you simply call one of the methods on that object.
the controller and jsp are linked together by #ModelAttribute.
example if i want to add a new student i would first link the corresponding jsp page with the student database. like
//setup add new student form
#RequestMapping(value = "/add", method = RequestMethod.GET)
public String setStudentForm(#ModelAttribute("newStudent") Student newStudent){
return "addstudent";
}
this will set up my jsp page. In jsp page i have already declared the colums like firstname, lastname which are linked with my student model.
<form:form modelAttribute="newStudent" class="form-horizontal">
<form:input id="firstName" path="firstName" type="text"/>
<form:input id="lastName" path="lastName" type="text"/>
<input type="submit" id="btnAdd" value ="add"/>
</form>
Like this i created the ling. Now when i press submit button if will land a post request and thus following method in controller will be executed.
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String processStudentForm(Model model, #ModelAttribute("newStudent") #Valid Student newStudent, BindingResult result ){
newStudent.setFirstName("gurpreet");
if(result.hasErrors()){
return "addstudent";
}
studentService.add(newStudent);
model.addAttribute("message", "Added successfully");
return "redirect:/students";
}
I can also make changes in the data like i did newStudent.setFirstName("gurpreet"); before saving the object through studentService
the #RequestMapping url is same but method has changed to POST from GET as submit button have POST submission.
(#ModelAttribute("newStudent") Student newStudent)
associates our view, Model and controller alltogether.

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.

Retrieving values from jsp without using pojo variables

We can get the values of a submitted form in a jsp page in controller using request.getParamenter(xxxx),by using commandName or by using hidden fields.
is there any other way to get values from a form of jsp in a controller?
Spring provides several ways of databinding parameters in your request to actual objects in Java. Most of the databinding is specified using annotated methods or by annotating paramters within methods.
Lets consider the following form:
<form>
<input name="firstName"/>
<input name="lastName"/>
<input name="age"/>
</form>
In a Spring controller the request parameters can be retreived in several ways.
#RequestParam Documentation
#RequestMapping("/someurl)
public String processForm(#RequestParam("firstName") String firstName,
#RequestParam("lastName") String lastName,
#RequestParam("age") String int,) {
.....
}
If our request parameters are modeled in a class Person.java we can use another technique, #ModelAttribute.
Person.java
public class Person(){
String firstName;
String lastName;
int age;
//Constructors and Accessors implied.
}
#ModelAttribute Documentation
#RequestMapping(value="/someUrl")
public String processSubmit(#ModelAttribute Person person) {
//person parameter will be bound to request parameters using field/param name matching.
}
These are two of the most commonly used methods Spring uses to provide databinding. Read up on others in the Spring MVC Documentation.
public String myMethod(#RequestParam("myParamOne") String myParamOne) {
//do stuff
}
You can directly map fields to controller method by annotation #RequestParam or you can directly bind object using #ModelAttribute.
public ModelAndView method(#RequestParam(required = true, value = "id") Long id) {
}
public ModelAndView method(#ModelAttribute("pojo") POJO pojo, BindingResult results) {
}

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.

Spring MVC form:errors not showing up

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!

Resources