RedirectView vs redirect: in spring mvc - spring

which is better to use to redirect url in spring mvc:
return new ModelAndView(new RedirectView("../abc/list.vm"));
or
return new ModelAndView("redirect:DummyRedirectPage.htm");

The ModelAndView object holds an instance variable that references its view object and which may be either:
A concrete View implementation such as the case when you use:
new ModelAndView(new RedirectView("../abc/list.vm"))
A String object holding the symbolic name of the view prefixed by redirect: or forward:, such is the case when using:
new ModelAndView("redirect:DummyRedirectPage.htm")
Now when the Spring base web entry, DispatcherServlet, is called to render a View, it will try to resolve the requested view against the given ModelAndView object, either getting the reference to its View sub-implementation it its afforded or resolving the View and creating it out of the ModelAndView view string representation:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
View view;
if (mv.isReference()) {
// Resolve the view and instantiate it...
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
// ...
}
// ...
}
Note that org.springframework.web.servlet.ModelAndView#isReference returns true if the view is a String object.
Given below details, I would assume that the first ModelAndView would involve less computation thus may be prefered to the later.

Related

Neither BindingResult nor plain target object for bean name AFTER redirect

I have a form like this (in a page called add.jsp):
<form:form action="${pageContext.request.contextPath}/add" method="post" modelAttribute="addForm">
</form:form>
On GET request, i populate modelAttribute:
#RequestMapping(value ="add", method = RequestMethod.GET)
public ModelAndView add(Map<String, Object> model) {
model.put("addForm", new AddUserForm());
return new ModelAndView("add");
}
When i perform the form submitting (a POST request), i have the follow method:
#RequestMapping(value ="add", method = RequestMethod.POST)
public ModelAndView add(Map<String, Object> model, #Valid AddUserForm form, Errors errors) {
if (errors.hasErrors()) {
//model.put("addForm", new AddUserForm());
return new ModelAndView("add");
}
....
}
But i get this error: Neither BindingResult nor plain target object for bean name 'addForm' available as request attribute
My workaround is to add model.put("addForm", new AddUserForm());, the command that i have commented on POST request.... but... where is my error ?
In both case, you are returning the same view (i.e. "add") and this view contains a form with a modelAttribute="addForm" therefore the model MUST contains an object named "addForm".
If you don't wan't to populate your model with a new AddUserForm after a POST with errors, you probably should :
return another view (without the "addForm" model attribute)
or
reuse the same "addForm": model.put("addForm", form);

Spring Mvc ModelAttribute with referencing name is not working?

I want to to create different #Entity entities within the same Controller.
#RequestMapping(value="create", method=RequestMethod.GET)
public String GET(Model model) throws InstantiationException, IllegalAccessException
{
Class<?> clazz = ????; // a Random POJO is chosen, i want to use POJOs!!
Object object = clazz.newInstance();
model.addAttribute("object", object);
return "create";
}
#RequestMapping(value="create", method=RequestMethod.POST)
public #ResponseBody Object POST(#ModelAttribute(value="object") Object object)
{
System.out.println("POST! got type: " + object.getClass().getName());
return object;
}
In the Post Method I get NULL for #ModelAttribute(value="object") Object object
If I change it to #ModelAttribute(value="object") realType object it is working perfectly fine. But I don't know the type yet.
I thought the #ModelAttribute can achieve this anyway with the name "object" but apparently not. What am I missing?
There is no actual model object named object when you submit, spring constructs it based on the parameter type and will bind the properties accordingly.
You have 2 choices to make it work
Store the object in the session
Use a #ModelAttribute annotated method
If neither of these are there spring will simply look at the method argument and use reflection to construct an instance of that class. So in your case it will only be Object and after that binding will fail.
Store object in the session
#Controller
#SessionAttributes("object")
public class MyController { ... }
Make sure that when you are finished that you call the setComplete() method on a SessionStatus object.
Use a #ModelAttribute annotated method
Instead of creating and adding the object in a request handling method create a speficic method for it.
#ModelAttribute("object")
public Object formBackingObject() {
Class<?> clazz = ????; // a Random POJO is chosen, i want to use POJOs!!
Object object = clazz.newInstance();
return object;
}
This method will be called before each request handling method, so that a newly fresh object is constructed which will be used for binding.

Spring MVC how to create controller without return (String) view?

The below is my sample controller.
#RequestMapping(value = "/validate", method = RequestMethod.POST)
public String validatePage1 (#ModelAttribute("page1")
Page1 pg1, BindingResult result) {
System.out.println("Value1:" + pg1.getVal1() +
"Value2:" + pg1.getVal2());
return "page2"; // I don't want to take any action (page navigation) here
}
#RequestMapping("/page1")
public ModelAndView pageShow() {
return new ModelAndView("page1", "command", new Page1());
}
Now the question is, I don't want to take any action in the client side when the method (validatePage1) is called by Spring framework, how to do?
Actually I have loaded all required pages in my client side at loading time (to avoid repeated page load), so I dont want to take any page navigation action in the client side, I just want to do the 'data binding' to complete my business logic in server side.
When I return "" empty string in "validatePage1()", Spring framework throws exception " Request processing failed; nested exception is org.apache.tiles.definition.NoSuchDefinitionException:" since I am using tiles, I have to remove tiles configuration later since I am loading all files at first loading itself.
You can set the method to return void and annotate it with #ResponseBody, as suggested here. No need to deal with HttpServletResponse etc.
Straight from the documentation:
Supported method return types
The following are the supported return types:
[...]
void if the method handles the response itself (by writing the response content directly, declaring an argument of type ServletResponse / HttpServletResponse for that purpose) or if the view name is supposed to be implicitly determined through a RequestToViewNameTranslator (not declaring a response argument in the handler method signature).

Spring preprocess request in another controller method

In one of my controllers I have:
#RequestMapping(value = "search", method = RequestMethod.GET)
public ModelAndView searchUsers(HttpSession session, HttpServletRequest request) {
UiUserSearchCriteria userSearchCriteria = (UiUserSearchCriteria) session
.getAttribute("UsersController_userSearchCriteria");
if (null == userSearchCriteria) {
userSearchCriteria= defaultUserSearchCriteria;
}
// Here be dragons
return searchUsers(userSearchCriteria, new BeanPropertyBindingResult(userSearchCriteria,
"userSearchCriteria"), session, request);
}
#RequestMapping(value = "search", method = RequestMethod.POST)
public ModelAndView searchUsers(
#ModelAttribute("userSearchCriteria") UiUserSearchCriteria userSearchCriteria,
BindingResult bindingResult, HttpSession session, HttpServletRequest request) {
userSearchCriteriaValidator.validate(userSearchCriteria, bindingResult);
if (bindingResult.hasErrors()) {
// Here be dragons
return new ModelAndView("searchUsers");
}
ModelAndView result = new ModelAndView("redirect:listUsers");
PagedListHolder<UiUser> userList = new PagedListHolder<UiUser>(
usersService.searchUsers(userSearchCriteria));
userList.setPageSize(10);
userList.setSort(defaultSort);
userList.resort();
session.setAttribute("UsersController_userList", userList);
session.setAttribute("UsersController_userSearchCriteria", userSearchCriteria);
return result;
}
The logic is simple: when the user navigates to search page I actually perform a search with default criteria and return him a list (this is the result of requirements changing, huh).
I found a problem in this code, accidentally. When default search criteria is not valid the behavior is: navigate to search -> populate search criteria with invalid criteria -> call another method (the second one, with POST) -> perform validation -> errors are not empty, so return searchUsers view. But the BindingResult bindingResult is actually syntethic, from previous method (new BeanPropertyBindingResult(userSearchCriteria, "userSearchCriteria")). So I got an error No binding result is bound to session (I agree with this).
I cannot have #ModelAttribute and BindingResult parameters (that, which are bound by Spring) pair in GET method to call POST with them.
So what is the best solutions for this?
I think you can simply associate your new BeanPropertyBindingResult(userSearchCriteria,
"userSearchCriteria") with an appropriate Spring model attribute name, this way:
BindingResult bindingResult = new BeanPropertyBindingResult(userSearchCriteria, "userSearchCriteria")
model.addAttribute(BindingResult.MODEL_KEY_PREFIX + "userSearchCriteria", bindingResult);
This is the default Spring MVC behavior of binding the validation results of a specific model attribute and should help you avoid the No binding result.. error

Freemarker custom method - get ModelAndView without explicitely passing it

I have written my own url routing mechanism, which allows mapping URLs to ModelAndViews and ModelAndViews back to URL (https://github.com/knyttl/Maite/wiki/Maite-Routing).
I am trying to create a Freemarker method which would modify the current ModelAndView and call the reverse routing process.
So in the template I would like to achieve something like:
${link("view", [id:10, page:1])}
Then the macro would be defined something like:
#Autowired
HandlerAdapter ha;
public TemplateModel exec(List args) throws TemplateModelException {
ModelAndView current = __getItSomehowFromTheTemplate();
if (current.getViewName() != (String) args.get(0)) {
// if the view is the same, we just modify the model
...
current.set...
} else {
// the view is different, we create a new ModelAndView
current = new ModelAndView();
}
// reverse routing process
return new SimpleScalar(ha.constructUrl(current));
}
I wonder whether I have to pass the ModelAndView to the method each time or I can let Freemarker somehow pass it automatically like with my magic method __getItSomehowFromTheTemplate();
If I have to pass it automatically, how can I do that? I did not find any direct reference to the current ModelAndView.
Thanks
How do you get the current ModelAndView in a template? From the data-model (template context) maybe? Because then you could call Environment.getCurrentEnvironment().getDataModel().get("theVariableName") to get it. (See: http://freemarker.org/docs/api/freemarker/core/Environment.html)

Resources