What is the difference between return ModelAndView and return String in Spring MVC? - spring

I want to know the different between ModelAndView and String.
#RequestMapping(value="/")
public ModelAndView mainPage() {
return new ModelAndView("home");
}
and the second piece of code is about returning String:
#RequestMapping(value="/")
public String mainPage() {
return "home";
}

You can return many things from a controller, and Spring will automatically try to deduce what to do from there.
When you return a ModelAndView all information that needs to be passed from the controller is embedded inside it. No suprise there.
If you return a String, it will assume that this is the name of the view to use. It will wrap this in a ModelAndView object with the string as the view and the existing model embedded as well.
Spring does a lot of 'magic' on the return types, but also on the parameter types.
This allows you to write code in a style that is more intuitive for you, i.e. the following two examples are the same:
#RequestMapping(value="/")
public ModelAndView mainPage() {
Model m = new Model();
m.put("key", "value");
return new ModelAndView(m,"main");
}
and
#RequestMapping(value="/")
public String mainPage(Model m) {
m.put("key", "value");
return "main";
}
This second variant is a little less verbose and easier to test separately. The model passed in will be an empty model (unless forwarded from some other controller).

Related

How to return jsp from ajax call?

I am using spring mvc without annotations.
I want to take jsp(html code) as response from ajax call.
I do not want to use response.getWriter().print(..). can any one tell me any other solution.?
You can return JSP using ModelAndView like this
#RequestMapping (
value = "/path/call",
method = RequestMethod.POST
)
#ResponseBody
public ModelAndView blah(....) {
return new ModelAndView("location to JSP file");
}
You could add data to MandV using the method below
/**
* Add an attribute to the model.
* #param attributeName name of the object to add to the model
* #param attributeValue object to add to the model (never {#code null})
* #see ModelMap#addAttribute(String, Object)
* #see #getModelMap()
*/
public ModelAndView addObject(String attributeName, Object attributeValue) {
getModelMap().addAttribute(attributeName, attributeValue);
return this;
}
i highly recommend reading the documentation, without actual knowledge of the spring framework you will have a hard time using it ... As has already been mentioned, you will usually have a Controller class which handles requests - these are annotated with #RequestMapping and the controller is annotated with #Controller, of course. This is an example from the documentation :
#Controller
#RequestMapping("/appointments")
public class AppointmentsController {
private final AppointmentBook appointmentBook;
#Autowired
public AppointmentsController(AppointmentBook appointmentBook) {
this.appointmentBook = appointmentBook;
}
#RequestMapping(method = RequestMethod.GET)
public Map<String, Appointment> get() {
return appointmentBook.getAppointmentsForToday();
}
#RequestMapping(value="/{day}", method = RequestMethod.GET)
public Map<String, Appointment> getForDay(#PathVariable #DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
return appointmentBook.getAppointmentsForDay(day);
}
#RequestMapping(value="/new", method = RequestMethod.GET)
public AppointmentForm getNewForm() {
return new AppointmentForm();
}
#RequestMapping(method = RequestMethod.POST)
public String add(#Valid AppointmentForm appointment, BindingResult result) {
if (result.hasErrors()) {
return "appointments/new";
}
appointmentBook.addAppointment(appointment);
return "redirect:/appointments";
}
}
As you can see, requests are now semi-automatically resolved / passed to JSP-parsers which process your stored JSP and output HTML. That is called MVC and although the MVC-model in spring differs a bit from the standard point of view its quite useful and somewhat standard'ish.
Yet again : if you want to use spring, please read the documentation. It is important and useful.
spring mvc without annotations
pretty much defeats the whole concept. I think you need to re-do your application design, apparently it is flawed --- no offense, im just stating the obvious.

Can't access Model attributes during validation

I'm using Spring 3.1 and have web pages using validation. The field-level validation, and the display of errors, works OK. My problem is with Model attributes not being available during the validation form display.
Let's say I've code:
#RequestMapping(value="/edit", method=RequestMethod.GET)
public String getEdit(#RequestParam("id") Long id, Model model) {
model.addAttribute("mytitle", "Hello There");
return "editObject"
}
#RequestMapping(value="/edit", method=RequestMethod.POST)
public String postEdit(#RequestParam("id") Long id, #Valid #ModelAttribute("object") MyData object, BindingResult result) {
if(result.hasErrors()) {
return "editObject";
}
[snip]
}
If I have an error the hasErrors() is detected and short-circuits to the map "editObject". However, the model attributes aren't available.
What do I use here? I tried adding a Model reference to the postEdit parameter list and adding in again things like the "mytitle" attribute.
Thanks,
Jerome.
When postEdit controller render the editObject view, you are in a new request, so you are loosing the model (previously set for getEdit action).
What you need to do it re-set any values needed in a new model:
#RequestMapping(value="/edit", method=RequestMethod.POST)
public String postEdit(#RequestParam("id") Long id, #Valid #ModelAttribute("object") MyData object, BindingResult result, Model model) {
if(result.hasErrors()) {
model.addAttribute("mytitle", "Hello There");
model.addAttribute("object", object);
return "editObject";
}
If you need to keep those attributes for different views you can store them in a session for example (so that you won't have to re-set them for each new request).

Force Initialization of #ModelAttributes in Spring MVC 3.1

I am writing a wizard-like controller that handles the management of a single bean across multiple views. I use #SessionAttributes to store the bean, and SessionStatus.setComplete() to terminate the session in the final call. However, if the user abandons the wizard and goes to another part of the application, I need to force Spring to re-create the #ModelAttribute when they return. For example:
#Controller
#SessionAttributes("commandBean")
#RequestMapping(value = "/order")
public class OrderController
{
#RequestMapping("/*", method=RequestMethod.GET)
public String getCustomerForm(#ModelAttribute("commandBean") Order commandBean)
{
return "customerForm";
}
#RequestMapping("/*", method=RequestMethod.GET)
public String saveCustomer(#ModelAttribute("commandBean") Order commandBean, BindingResult result)
{
[ Save the customer data ];
return "redirect:payment";
}
#RequestMapping("/payment", method=RequestMethod.GET)
public String getPaymentForm(#ModelAttribute("commandBean") Order commandBean)
{
return "paymentForm";
}
#RequestMapping("/payment", method=RequestMethod.GET)
public String savePayment(#ModelAttribute("commandBean") Order commandBean, BindingResult result)
{
[ Save the payment data ];
return "redirect:confirmation";
}
#RequestMapping("/confirmation", method=RequestMethod.GET)
public String getConfirmationForm(#ModelAttribute("commandBean") Order commandBean)
{
return "confirmationForm";
}
#RequestMapping("/confirmation", method=RequestMethod.GET)
public String saveOrder(#ModelAttribute("commandBean") Order commandBean, BindingResult result, SessionStatus status)
{
[ Save the payment data ];
status.setComplete();
return "redirect:/order";
}
#ModelAttribute("commandBean")
public Order getOrder()
{
return new Order();
}
}
If a user makes a request to the application that would trigger the "getCustomerForm" method (i.e., http://mysite.com/order), and there's already a "commandBean" session attribute, then "getOrder" is not called. I need to make sure that a new Order object is created in this circumstance. Do I just have to repopulate it manually in getCustomerForm?
Thoughts? Please let me know if I'm not making myself clear.
Yes, sounds like you may have to repopulate it manually in getCustomerForm - if an attribute is part of the #SessionAttributes and present in the session, then like you said #ModelAttribute method is not called on it.
An alternative may be to define a new controller with only getCustomerForm method along with the #ModelAttribute method but without the #SessionAttributes on the type so that you can guarantee that #ModelAttribute method is called, and then continue with the existing #RequestMapped methods in the existing controller.

Spring MVC Controllers Return Type

I've seen examples where a controller returns a String (which indicates the view)
#RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(#PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner"
}
And I also see examples where a controller returns a "ModelAndView" object type
public ModelAndView helloWorld() {
ModelAndView mav = new ModelAndView();
mav.setViewName("helloWorld");
mav.addObject("message", "Hello World!");
return mav;
}
What is the difference between the two and which should I use? Cause either way I can get my view resolved.
It's the same logic but it's not the same version of spring.
The ModelAndView object is the spring 2.x way of handling model and views.
In the example you gave, the modelandview object will load the "helloWorld" view (depending on your templating engine could be helloWorld.jsp, or helloWorld.html, ...) with one data "message" in the model.
The other way is the spring 3.x way. You could have wrote exactly the same example as your helloworld.
#RequestMapping(value="/helloWorld", method=RequestMethod.GET)
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World!");
return "helloWorld";
}
The model is automaticly populated at request.
And we can simplify this notation as the url mapping "helloWorld" is directly the view name.
#RequestMapping(value="/helloWorld", method=RequestMethod.GET)
public void helloWorld(Model model) {
model.addAttribute("message", "Hello World!");
}
the helloWorld view will be automaticly loaded
If we are talking about MVC 3, than, both are correct. But directly returning ModelAndView is the old way, and more verbal.
If you are returning just a string (without #ResponseBody which is something else), this string is treated as view name, and spring pushes it to view resolvers - so, you dont have to worry (at least, while you are writing controllers), what type of view renderer you'll use (let it be jsp or velocity, it doesn't matter). You only propagate the Model instance, and returnes a hint what to do with it next. Proper ModelAndView object is made later internally by string.
Generally, spring 3 gives you more flexibility with arguments and return types (see Defining #RequestMapping handler methods section in Spring documentaton).
In Spring MVC, you should return ModelAndView if you want to render jsp page
For example:
#RequestMapping(value="/index.html", method=RequestMethod.GET)
public ModelAndView indexView(){
ModelAndView mv = new ModelAndView("index");
return mv;
}
this function will return index.jsp when you are hitting /index.html
In addition you can return any JSON or XML object using #ResponseBody annotation and serializer.
For example:
#RequestMapping(value="/getStudent.do",method=RequestMethod.POST)
#ResponseBody
public List<Student> getStudent(#RequestParam("studentId") String id){
List<Student> students = daoService.getStudent(id);
return students;
}
In this example you will return List as JSON in case and you have enabled Jackson serializer. In order to enable that you need to add the following to your Spring XML:
<context:annotation-config/>
And the Serializer itself:
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jacksonMessageConverter"/>
</list>
</property>
</bean>
Hope it helps.
Returning ModelAndView or a string does the same job. It resolves into a view (a jsp page) and giving a model object to be rendered in the view. If you return a string, spring internally resolves to a view using viewResolver, create a ModelAndView Object and return it. The returning of ModelAndView object is from the older version. You can do either of this based on your convenient.
Have a look at this question
Also bunch of answers are pointing out to #RequestBody. Its not exactly related to your question. But keep in mind that it will convert your object based on content-type using available converters and the resuly will be part of the document body.
If you are using a template language like freemarker or velocity to create the response then you can return the ModelAndView.
If you want to return a json/xml or some string as the response(ex: ajax requests) then you can use the first method. I think it should be public #ResponseBody String findOwner(#PathVariable String ownerId, Model model), you need to add a annotation called #ResponseBody to indicate that the returned value should be converted as the response text. Using #ResponseBody will enable you to use libraries like Jackson or JaxB to create json/xml responses respectively.
These returned values will be converted using the messageConverters registered with AnnotationMethodHandlerAdapter.
Ref: http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/
public String findOwner(#PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner"
}
in this method the return type is String and we adding Model as a parameter so to add a value with model we will add like
modelparam.addAttribute("obj",value);
and return to the displayowner to jsp as per view resolver
public ModelAndView helloWorld() {
ModelAndView mav = new ModelAndView();
mav.setViewName("helloWorld");
mav.addObject("message", "Hello World!");
return mav;
}
in this method the return type is ModelAndView so we will return the model
and here the mav is the object of ModelAndView so here we should add like
model.addObject("object","value");
here the viewname is returned to helloworld.jsp as per the viewResolver

Spring Framework 3 and session attributes

I have form object that I set to request in GET request handler in my Spring controller. First time user enters to page, a new form object should be made and set to request. If user sends form, then form object is populated from request and now form object has all user givern attributes. Then form is validated and if validation is ok, then form is saved to database. If form is not validated, I want to save form object to session and then redirect to GET request handling page. When request is redirected to GET handler, then it should check if session contains form object.
I have figured out that there is #SessionAttributes("form") annotation in Spring, but for some reason following doesnt work, because at first time, session attribute form is null and it gives error:
org.springframework.web.HttpSessionRequiredException: Session attribute 'form' required - not found in session
Here is my controller:
#RequestMapping(value="form", method=RequestMethod.GET)
public ModelAndView viewForm(#ModelAttribute("form") Form form) {
ModelAndView mav = new ModelAndView("form");
if(form == null) form = new Form();
mav.addObject("form", form);
return mav;
}
#RequestMapping(value="form", method=RequestMethod.POST)
#Transactional(readOnly = true)
public ModelAndView saveForm(#ModelAttribute("form") Form form) {
FormUtils.populate(form, request);
if(form.validate())
{
formDao.save();
}
else
{
return viewForm(form);
}
return null;
}
It throws Exception if controller called first time even though added #SessionAttributes({"form"}) to class. So add following populateForm method will fix this.
#SessionAttributes({"form"})
#Controller
public class MyController {
#ModelAttribute("form")
public Form populateForm() {
return new Form(); // populates form for the first time if its null
}
#RequestMapping(value="form", method=RequestMethod.GET)
public ModelAndView viewForm(#ModelAttribute("form") Form form) {
ModelAndView mav = new ModelAndView("form");
if(form == null) form = new Form();
mav.addObject("form", form);
return mav;
}
#RequestMapping(value="form", method=RequestMethod.POST)
#Transactional(readOnly = true)
public ModelAndView saveForm(#ModelAttribute("form") Form form) {
// ..etc etc
}
}
The job of #SessionAttribute is to bind an existing model object to the session. If it doesn't yet exist, you need to define it. It's unnecessarily confusing, in my opinion, but try something like this:
#SessionAttributes({"form"})
#Controller
public class MyController {
#RequestMapping(value="form", method=RequestMethod.GET)
public ModelAndView viewForm(#ModelAttribute("form") Form form) {
ModelAndView mav = new ModelAndView("form");
if(form == null) form = new Form();
mav.addObject("form", form);
return mav;
}
#RequestMapping(value="form", method=RequestMethod.POST)
#Transactional(readOnly = true)
public ModelAndView saveForm(#ModelAttribute("form") Form form) {
// ..etc etc
}
}
Note that the #SessionAttributes is declared on the class, rather than the method. You can put wherever you like, really, but I think it makes more sense on the class.
The documentation on this could be much clearer, in my opinion.
if there is no defined session object so I think it's gonna be like this:
#SessionAttributes({"form"})
#Controller
public class MyController {
#RequestMapping(value="form", method=RequestMethod.GET)
public ModelAndView viewForm() {
ModelAndView mav = new ModelAndView("form");
if(form == null) form = new Form();
mav.addObject("form", form);
return mav;
}
#RequestMapping(value="form", method=RequestMethod.POST)
#Transactional(readOnly = true)
public ModelAndView saveForm(#ModelAttribute("form") Form form) {
// ..etc etc
}
}
#Controller
#SessionAttributes("goal")
public class GoalController {
#RequestMapping(value = "/addGoal", method = RequestMethod.GET)
public String addGoal(Model model) {
model.addAttribute("goal", new Goal(11));
return "addGoal";
}
#RequestMapping(value = "/addGoal", method = RequestMethod.POST)
public String addGoalMinutes(#ModelAttribute("goal") Goal goal) {
System.out.println("goal minutes " + goal.getMinutes());
return "addMinutes";
}
}
On page addGoal.jsp user enters any amount and submits page. Posted amount is stored in HTTP Session because of
#ModelAttribute("goal") Goal goal
and
#SessionAttributes("goal")
Without #ModelAttribute("goal") amount entered by user on addGoal page would be lost
I'm struggling with this as well. I read this post and it made some things clearer:
Set session variable spring mvc 3
As far as I understood it this basically says:
that Spring puts the objects specified by #SessionAttributes into the session only for the duration between the first GET request and the POST request that comes after it. After that the object is removed from the session. I tried it in a small application and it approved the statement.
So if you want to have objects that last longer throughout multiple GET and POST requests you will have to add them manually to the HttpSession, as usual.

Resources