Spring-MVC 3.1: Forwarding A Request From One Controller Function To Another - spring-mvc

I'm using Spring 3.1. I have a controller function that takes in a command object ( a data holder ) submitted via a FORM and does some processing :
#RequestMapping(value = "/results", method = RequestMethod.POST)
public String toResultsScreen(#ModelAttribute("ssdh") SearchScreenDataHolder ssdh,
BindingResult bindingResult,
ModelMap model,
HttpSession session) {
if (bindingResult.hasErrors()) {
logger.debug("Error returning to /search screen");
return "search";
}
netView = "results";
// do stuff
return nextView;
} // end function
Some user would like to programmatically make GET links to obtain information from our site and I would like to set up another handler that would handle that request. It would create a new installation of that the command object ( ssdh ) and populate it with the parameters sent via the GET request. Then it would pass it on to the handler above. Something like this:
#RequestMapping(value = "/pubresult")
public String toPublicResultsScreen(ModelMap model,
HttpSession session,
#RequestParam (required=true) String LNAME,
#RequestParam (required=false)String FNAME){
Search search = new Search(usertype);
// Capture the search parameters sent by HTTP
ssdh.setLast_name(LNAME);
ssdh.setFirst_name(FNAME);
// To Do: "forward this data holder, ssdh to the controller function quoted first
return nextView;
} // end function
My question is how can I forward my command/data holder object to the first controller function such that I don't have to alter the code to the first controller function in any way?

You can use RedirectAttributes object which was introduced in Spring MVC 3.1 and populate it with data you want to keep for redirection. It called PRG (POST/Redirect/GET) pattern.
#RequestMapping(value="/saveUserDetails.action", method=RequestMethod.POST)
public String greetingsAction(#Validated User user,RedirectAttributes redirectAttributes){
//setting attributes
redirectAttributes.addFlashAttribute("firstName", user.getFirstName());
redirectAttributes.addFlashAttribute("lastName", user.getLastName())
return "redirect:success.html";
}
I wrote some technical article regarding how to use it. I believe it will give you more details:
http://www.tikalk.com/java/redirectattributes-new-feature-spring-mvc-31

You should be able to set the ssdh in a ModelAttribute and simply forward it back, this way, the RequestDispatcher should be able to map it back to the /results handler:
#RequestMapping(value = "/pubresult")
public String toPublicResultsScreen(ModelMap model,
HttpSession session,
#RequestParam (required=true) String LNAME,
#RequestParam (required=false)String FNAME, Model model){
Search search = new Search(usertype);
// Capture the search parameters sent by HTTP
ssdh.setLast_name(LNAME);
ssdh.setFirst_name(FNAME);
model.addAttribute("ssdh", ssdh);
return "forward:/results";
}

Use
org.springframework.web.servlet.view.RedirectView
class from spring package to redirect to different page in spring MVC controller. The Baeldung blog page has more details
Sample code:
#RequestMapping(value = "/", method = RequestMethod.GET)
public RedirectView mainMethod() {
return new RedirectView("/login");
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView mainLogin() {
ModelAndView model = new ModelAndView("login");
return model;
}

Related

Spring RequestMapping: Get path with slashes

Is there a way to get the first part of the url path, before /c:
Thats it: /Category1/Subcategory1.1/Subcategory1.1.1/c/{categoryCode:.*}
#Controller
#RequestMapping(value = "/**/c")
public class CategoryPageController {
#RequestMapping(value = "/{categoryCode:.*}", method = RequestMethod.GET)
public ModelAndView viewCategory(#PathVariable String categoryCode,
final HttpServletRequest request) {
String categoryPath = ...
I tried to use AntPathMatcher (extractPathWithinPattern) but I'm not able to define the correct pattern.
inject the request object by simply adding the the HttpServletRequest to the method signature, and
then access the Request Url by calling getRequestURL().
(oh and auto converting answers to comments, stackoverflow jumps the shark)

prevent json of my command object from comming down to client during post

My controller has following calls
#ModelAttribute("commandObject")
public UsersCommand getCommand(HttpServletRequest req) throws Exception {
...
return command;
}
#RequestMapping(value = {"addusers.json"}, method = RequestMethod.GET)
public void handleGet() {
//empty method
}
#RequestMapping(value = {"addusers.json"}, method = RequestMethod.POST)
public void handlePost(#ModelAttribute("commandObject") UsersCommand command, HttpServletRequest req) throws Exception {
//do stuff
}
during the get i get the json of my UsersCommand object, however after I do a post, I am getting the json of my command object which i do not need as i want to do a fire and forget post.
How can I avoid the json object from coming down to browser during post?
It seems like you only want JSON returned with the GET method. To do this, remove the #ModelAttribute method all-together. This tells Spring to add the return object to your model on every handler in the controller, which you don't want. Then modify your GET handler to be like the following:
#RequestMapping(value = {"addusers.json"}, method = RequestMethod.GET)
#ResponseBody
public UserCommand handleGet() {
UserCommand cmd = getUserCommand();
return cmd;
}
The #ResponseBody annotation tells Spring to serialize the return type to JSON (or XML if you annotated your class with JAXB, and depending on the request accept headers). For this to work, you also need to add Jackson to your classpath and make sure you're using <mvc:annotation-driven /> or #EnableWebMvc in your XML or Java config, respectively.
This will sound like self-promotion, but I wrote a post about this if you want more detail: http://codetutr.com/2013/04/09/spring-mvc-easy-rest-based-json-services-with-responsebody/.
Hope that's helpful! Let me know if I can add any more clarity for you.

How to set all post data in ModelMap in Spring MVC?

I have many input fields in a form to post data to server side.
And in controller, I want to get all attributes in ModelMap like this:
public String save(ModelMap model) {
....
}
I don't want to use #RequestParam since there are so many fields to be sent. I think the data in a form will be posted and saved in ModelMap automatically, but its not working
Could anyone help me?
Thanks.
You should use a #ModelAttribute in the form handler
#RequestMapping(value="/submitform", method = RequestMethod.POST)
public String save(#ModelAttribute("mydata") MyData myData) {
//do something with
//myData.getField1();
//myData.getFiled2();
}
and this is how you will send the page to the form
#RequestMapping("/fillform")
public String loadForm(ModelMap model) {
//you could also fill MyData, to do autofill on the html form
model.put("mydata", new MyData());
return "fillform"; //[jsp]view resolver will pick up fillform.jsp
}
I am trying to make the same thing. The problem with previous answer is taht the UI is dynamically build by iterating on Map<String, Object>.
So it is not possible to pre define the MyData.class.
I am solving this issue using next approach:
#RequestMapping(value = "/secure/filled", method = RequestMethod.POST)
public String saveFilledData(HttpServletRequest request, ModelMap model) {
request.getParameter("nameOfTheParameter");
}

Spring 3 MVC Handle multiple form submit with a single Controller

Spring 3 MVC Handle multiple form submit with a Controller.
I am developing JSP page with multiple forms. 1) Search Customer, 2) Search Product, 3) Print Something etc. I've a different form bind object tied to each form and my controller code looks similar to below
#Controller
#RequestMapping(value="/search.do")
public class SearchController {
#RequestMapping(method = RequestMethod.GET)
public String pageLoad(ModelMap modelMap) {
modelMap.addAttribute("productSearch", new ProductSearchCriteria());
modelMap.addAttribute("customerSearch", new CustomerSearchCriteria());
modelMap.addAttribute("print", new PrintForm());
}
#RequestMapping(method = RequestMethod.POST)
public ModelAndView searchProducts(#ModelAttribute("productSearch") ProductSearchCriteria productSearchCriteria,
BindingResult result, SessionStatus status) {
//Do Product search
return modelAndView;
}
#RequestMapping(method = RequestMethod.POST)
public ModelAndView searchCustomers(#ModelAttribute("customerSearch") CustomerSearchCriteria customerSearchCriteria,
BindingResult result, SessionStatus status) {
//Do Customer search
return modelAndView;
}
#RequestMapping(method = RequestMethod.POST)
public ModelAndView printSomething(#ModelAttribute("print") PrintForm printForm,
BindingResult result, SessionStatus status) {
//Print something
return modelAndView;
}
}
Above certainly doesn't work as I assumed it would. I get exception saying 'Request method 'POST' not supported'. If I have only one POST method inside above controller say searchProducts it works well. But it won't with more than one methods with POST. I also tried adding hidden parameter in JSP and changing method signatures similar to below only to get the same exception again.
#RequestMapping(method = RequestMethod.POST, params="pageAction=searchProduct")
public ModelAndView searchProducts(#ModelAttribute("productSearch") ProductSearchCriteria productSearchCriteria,
BindingResult result, SessionStatus status) {
//Do Product search
return modelAndView;
}
Can anyone please suggest correct way to achieve above? Also any reference to source material or further reading will be greatly appreciated. Thanks.
EDIT #1: The above approach with params="pageAction=searchProduct" works perfectly as far as you get your hidden parameter right in JSP (see comment below). In addition to that, answers by #Bozho and #Biju Kunjummen is also very helpful and a good (possibly better?) alternative to tackle multiple form submit.
Your mappings are not completely correct #TMan:
The mappings in web.xml are to get Spring Dispatcher servlet to handle your request -
eg. like in your case:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/META-INF/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
appServlet
*.do
So now any request to URI's ending with .do will be handled by Spring's DispatcherServlet
The controller can have a mapping, like in your case:
#Controller
#RequestMapping(value="/search.do")
But where you have gone wrong is in the RequestMapping for the controller :
#Controller
#RequestMapping(value="/search")
The .do at the end of RequestMapping should not be there, as it is purely for the Dispatcher servlet to be invoked, once that is invoked it will handle dispatching to the correct Controller method, so your call to /search.do will end up with the controller.
Now, each method can be annotated with RequestMapping like in your case, with a RequestMethod atribute of RequestMapping, specifying whether to dispatch to the method in case of POST or GET:
#RequestMapping(method = RequestMethod.GET)
#RequestMapping(method = RequestMethod.POST)
So when there is a POST to the /search.do URI the appropriate method will be called.
In your case, there are multiple methods annotated with the RequestMethod.POST attribute, so the AnnotationMethodHandlerAdapter component of Spring simply doesn't know which method to dispatch to.
There are a couple of things that you can do:
put a request mapping for each method, the way suggested by #Bozho:
#RequestMapping(value="/customers" method=Request.POST)
#RequestMapping(value="/products" method=Request.POST)
so now your request URI's would be
/search/customers.do
/search/products.do
and you should be doing POST to get the correct dispatch method.
Get rid of method=Request.POST all together and depend on the #RequestMapping like above to find the correct method.
You can pass an optional params attribute to RequestMapping, which again is a way to help Spring find your correct method to dispatch to:
#RequestMapping(method=Request.POST, params="customers")
with customers parameters in the request or say products parameters in the request.
The simplest will be option 1 though.
EDIT 1: Adding a reference to a good Spring document - http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html
When you have multiple forms, why don't you map the three forms to different URLs?
#RequestMapping("/products")
public ModelAndView searchProducts(..)
#RequestMapping("/customers")
public ModelAndView searchCustomers(..)
And have your form actions be pointed at /search/products and /search/customers (no need of the .do)
I've had the same problem: two forms (register and contact) on the same page with two submit buttons.
Each form has its own validation by Spring.
I've managed to do this in one Controller:
#RequestMapping(value = "/contactOrRegister.do", method = RequestMethod.GET)
public void contactOrRegisterFormGet(#ModelAttribute("contactForm") ContactForm contactForm, #ModelAttribute("registerForm") RegisterForm registerForm) {
//-----prepare forms for displaying here----
}
#RequestMapping(value = "/contact.do", method = RequestMethod.POST)
public String contactFormPost(#ModelAttribute("contactForm") ContactForm contactForm, BindingResult result, final Model model) {
contactValidator.validate(contactForm, result);
if (result.hasErrors()) {
model.addAttribute("registerForm", new RegisterForm());
return "contactOrRegister";
}
//--- validation is passed, do submit for contact form here ---
}
#RequestMapping(value = "/register.do", method = RequestMethod.POST)
public String registerFormPost(#ModelAttribute("registerForm") RegisterForm registerForm, BindingResult result, final Model model) {
registerValidator.validate(registerForm, result);
if (result.hasErrors()) {
model.addAttribute("contactForm", new ContactForm());
return "contactOrRegister";
}
//--- validation is passed, do submit for register form here ---
}
I need to create a new form (contact or register) and put it in model when validation is failed because "contactOrRegister" view needs both forms to display. So when "contact" form was submitted and has errors the "register" form content will be erased. This way fits for me.
The contactOrRegister.jsp contains both forms with different action:
<form:form action="/register.do" modelAttribute="registerForm" method="POST">
<!-- register form here -->
</form:form>
<form:form action="/contact.do" modelAttribute="contactForm" method="POST">
<!-- contact form here -->
</form:form>
You can write
#RequestMapping(method = {RequestMethod.GET,RequestMethod.POST})
when using multiple method on the same controller. This is useful when sometimes the user wants to submit the data using GET or when you use return "forward:url"; from another controller.

How to manage validation in GET form and submit POST form?

the enviroment is Spring 3.0 with new function Vallidation.
I create an annotated controller (ResetUserPasswordController) which manages a showForm on HTTP.GET and the submit form on HTTP.POST. The function is a reset user password requested by email : the user access previously to another form, where i fill is email address and a recaptcha control, if recaptcha is correct, the user receive a mail with a link which contains a paramter. The two methods (on HTTP.GET, and HTTP.POST) have two different command bean have different paramters(i choice two differents beans to manage the validation process in two diffent validators classes). Probably you are questioning : why do you define two differents commands? I have defined the following role : Every bussiness and basic (like notnull validation etc) validation process must be managed by a validator class which supports a specific command bean
I want to create the istance of command bean managed by the POST, in the GET method, but during some tests I realized that this can be not correct beacuse if the validation process goes bad, I have all errors on the input command which is different from which i'm gogin to put in the returned ModelAndView.
Someone has some suggestion to manage correctly this scenario?
#RequestMapping(method = RequestMethod.POST)
public ModelAndView processSubmit(#Valid #ModelAttribute("command") ResetUserPasswordCommand command, BindingResult result, HttpServletRequest request, HttpServletResponse response) {
getValidator().validate(command, result);
if (result.hasErrors()) {
// TODO : implements error page.
return new ModelAndView();
} else {
Map<String, Object> model = new HashMap<String, Object>();
try {
PasswordChangeRequest passwordChangeRequest = getUserService().findPasswordChangeRequest(command.getUuid());
getUserService().updateUserPassword(command.getUuid(), command.getPassword());
autoLogin(request, response, passwordChangeRequest.getAccount(), command.getPassword());
} catch (ApplicationThrowable aex) {
return new ModelAndView("responseKO", model);
}
return new ModelAndView("Home", model);
}
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView setupForm(#Valid #ModelAttribute("command") ResetUserPasswordFormCommand command, BindingResult result) {
getFormValidator().validate(command, result);
if (result.hasErrors()) {
// TODO : implements error page.
return new ModelAndView();
} else {
Map<String, Object> model = new HashMap<String, Object>();
ResetUserPasswordCommand resetUserPasswordCommand = new ResetUserPasswordCommand();
resetUserPasswordCommand.setUuid(command.getUuid());
model.put("command", resetUserPasswordCommand);
model.put("reCaptchaHTML", getReCaptchaService().getReCaptchaObjectNoSSL().createRecaptchaHtml(null, null));
return new ModelAndView("user/ResetUserPassword", model);
}
}

Resources