Spring MVC autobinding can not access map data in thymeleaf - spring-boot

I am new to java and was trying to test thymeleaf in springboot.
with code like the first controller I can access the data in a map through thymeleaf
but can not access the data in a map by the second controller.
why declaring map like 2nd method can't work?
can someone help me? thx
public class ViewController {
public String index(Map<String, Object> map) {
map.put("hello","Bonjour");
map.put("Users", Arrays.asList("Ken","Yu","JY"));
return "index";
}
}
public class ViewController {
public String index() {
Map<String, Object> map=new HashMap<String, Object>();
map.put("hello","Bonjour");
map.put("Users", Arrays.asList("Ken","Yu","JY"));
return "index";
}
}

Springboot follows the Model View Controller (MVC) architecture . Simply put Models are used to supply attributes to the view to be rendered.Here you are using thyme-leaf as the view renderer while the model is fed into the thyme-leaf template.Model, ModelMap, and ModelAndView are used to define a model in a Spring MVC application.Spring boot automatically takes care of binding the view and the model together.
As such springboot #RequestMapping handler method expects the method argument for model as :
java.util.Map / org.springframework.ui.Model /
org.springframework.ui.ModelMap for enriching the implicit model that
is exposed to the web view.
Doc
So if you provide a map as a method parameter it is automatically bound the view.In your second case that it not happening automatically .That is the reason second method does not work.Example using ModelAndView :
#GetMapping("/goToIndexPage")
public ModelAndView methodWithModelAndViewReturn() {
ModelAndView modelAndView = new ModelAndView("index");
modelAndView.addObject("message", "Hello");
return modelAndView;
}

Related

What is the benefit of Model / ModelMap over ModelAndView in Spring MVC?

What is the benefit of Model / ModelMap over ModelAndView in Spring MVC? When we should use Model/ModelMap instead of ModelAndView.
I checked many sites but answers are not clear!
I have read few pages and answers and there doesn't seem to be any differences really. Matter of taste. ModelAndView object wraps model and view. It seems that that is also done implicitly when returning a String. Use the way you prefer.
You can read here about the return types:
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-return-types
Model
Let’s start with the most basic concept here – the Model. Simply put, the model can supply attributes used for rendering views.
To provide a view with usable data, we simply add this data to its Model object. Additionally, maps with attributes can be merged with Model instances:
#GetMapping("/showViewPage")
public String passParametersWithModel(Model model) {
Map<String, String> map = new HashMap<>();
map.put("spring", "mvc");
model.addAttribute("message", "Baeldung");
model.mergeAttributes(map);
return "viewPage";
}
ModelMap
Just like the Model interface above, ModelMap is also used to pass values to render a view. The advantage of ModelMap is it gives us the ability to pass a collection of values and treat these values as if they were on a Map:
#GetMapping("/printViewPage")
public String passParametersWithModelMap(ModelMap map) {
map.addAttribute("welcomeMessage", "welcome");
map.addAttribute("message", "Baeldung");
return "viewPage";
}
ModelAndView
The final interface to pass values to a view is the ModelAndView.
This interface allows us to pass all the information required by Spring MVC in one return:
#GetMapping("/goToViewPage")
public ModelAndView passParametersWithModelAndView() {
ModelAndView modelAndView = new ModelAndView("viewPage");
modelAndView.addObject("message", "Baeldung");
return modelAndView;
}

How to map Multiple controllers in Spring MVC

I have two controllers in my Application; one is userController, where I have add, delete and update methods; the other one is studentController, where I also have add, delete and update methods.
All the mappings are same in my methods using #RequestMapping annotation in both controllers. I have one confusion: if we are passing the same action from the JSP, then how will the Dispatcher find the corresponding controller? If anybody could describe this using example will be appreciated.
You have to set a #RequestMapping annotation at the class level the value of that annotation will be the prefix of all requests coming to that controller,
for example:
you can have a user controller
#Controller
#RequestMapping("user")
public class UserController {
#RequestMapping("edit")
public ModelAndView edit(#RequestParam(value = "id", required = false) Long id, Map<String, Object> model) {
...
}
}
and a student controller
#Controller
#RequestMapping("student")
public class StudentController {
#RequestMapping("edit")
public ModelAndView edit(#RequestParam(value = "id", required = false) Long id, Map<String, Object> model) {
...
}
}
Both controller have the same method, with same request mapping but you can access them via following uris:
yourserver/user/edit
yourserver/student/edit
hth
We can have any number of controllers, the URL mapping will decide which controller to call..
Please refer here for detailed Spring MVC multiple Controller example

Intercepting the #responsebody in spring mvc

I have a Spring MVC web application with conroller like below :
#Controller
public class ActionRestController {
#RequestMapping(value = "/list", method = GET)
#ResponseBody
public List<Action> list(Action action, SearhCriteria searchCriteria) {
List<Action> ret = new ArrayList<Action>();
// Call a service method to get the records
// Copy the records into the list
// return the list of objects
return ret;
}
The Controller is invoked when the user does a search. There are several such controllers in the app, one for each searchable entity.
For reasons that I cannot explain very well, here, I cannot modify these controllers in anyway.
But now, I have requirement in the UI to display the search criteria and the no. of records and paging details, as well. This information is not returned by the controller. The JSON returned by the Controller contains just the list of records.
I have put up a different controller which will handle the request, gets and puts the extra info in the model and forwards the request to the existing controller like below :
#Controller
public class ActionExtendedController {
#RequestMapping(value = "/searchlist", method = GET)
#ResponseBody
public List<Action> list(Action action, SearhCriteria searchCriteria, Model model) {
model.addAttribute("searchParameters", searchCriteria);
return "forward:/list";
}
Upto this point, all is well.
What I want to do is intercept the request at a point where the List is returned from the controller, before it is converted to JSON, and return a map containing the list and the search parameters.
Now since the 'immutable' controller users ResponseBody the control goes to the JacksonMessageConverter amd the response goes out from there. I have already tried the following paths and they do not work.
Interceptor - By the time I get here, the response is already written out, so there is no way to change it.
Custom ObjectMapper for the JasksonMessageConverter - Will not work, since I do not have access to the model object inside the mapper, I only have access to the list returned by the controller.
Aspect #After pointcut for the controller - I think this technique will work, but I cannot get it to work. The advise does not fire and I am sure I am missing something in the configuration.
Is there a way to get Spring AOP to fire on a annotated controller, handler method or
can anyone suggest another method of intercepting the handler return value (along with the model) ?
How about a simple delegation to the base controller in your extended controller:
#Controller
public class ActionExtendedController {
#Autowired ActionRestController baseRestController;
#Autowired MappingJacksonJsonView mappingJacksonJsonView;
#RequestMapping(value = "/searchlist", method = GET)
public View list(Action action, SearhCriteria searchCriteria, Model model) {
List<Action> actions = baseRestController.list(action, searchCriteria, model);
model.addAttribute("actions", actions);
model.addAttribute("searchParameters", searchCriteria);
return mappingJacksonJsonView;
}
this way you are delegating to the original controller, but using this new controller for the view. Just register a mappingJacksonJsonView as a bean also which will serialize all model objects (searchcriteria and actions) into the json view. You need not even return a view but can also use #ResponseBody, with a type that can hold the responses and search criteria.
Why don't you change the return type to a Map? Like:
#Controller
public class ActionRestController {
#RequestMapping(value = "/list", method = GET)
#ResponseBody
public Map<String, Object> list(Action action, SearhCriteria searchCriteria) {
Map<String, Object> map = new HashMap<String, Object>();
List<Action> ret = new ArrayList<Action>();
// Call a service method to get the records
// Copy the records into the list
// return the list of objects
map.put("searchResult",ret);
map.put("searchCriteria", searchCriteria);
return map;
}

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 SimpleFormController with annotations in referenceData

How can i write referenceData method with SpringFormController annotations.I have several java.util.Map objects ,Finally i am setting all these objects in coomand object.How to return this object by using Spring Form Controller.
Initially I am displaying form using below code:
#RequestMapping(method=RequestMethod.GET)
public String initForm(Model map){
TestDTO test=new TestDTO();
map.attribute("cmdtest",test);
return "test";
}
#ModelAttribute("customer")
public Model setup(Model map,HttpServletRequest request)
{
Map testData=testService.getTestData(request)
model.addAttribute("testData",testData);
return model;
}
How to access testData map object in my jsp page?
Regards,
Raj
The map is available to the jsp page as customer.testData; "customer" because that's what you named your ModelAttribute and "testData" because that's what you named it in the model.
<p>Here is your testData: ${customer.testData}</p>

Resources