Spring 3 : Binding same controller method to multiple form action - spring

I have a controller method with RequestMapping.PUT and having a URI
#RequestMapping(value = "/add/email", method = RequestMethod.POST)
public String addNewAccountEmail(#Valid #ModelAttribute EmailClass emailObject, BindingResult bindingResult, Model model) {
return displayForm(model);
}
I have a form like :
<form:form id="add-user-email" action="/add/email" name="manageUserAddEmail" method="post" modelAttribute="accountEmail">
I want to have more form pointing to same action , but need to do different operations inside addNewAccountEmail method. So how can I achieve this in Spring ? Basically any parameter which can make me differentiate functionalities or somehow I can have multiple methods having same RequestMapping URL and Method ?
I can only use RequestMethod.POST as I have similar requirements for other methods as well.
Basically I do not want the URL to change in Browser when invoking actions, that is why I want all form actions to point to same action URL.

You could point all of your forms at the same controller method and then differentiate the form-specific functionality within that method by looking for form-specific request parameters.
Each form would need to add its own request parameter to identify it - such as:
<input type="hidden" name="form1_param" value="1"/>
And then you can vary the behaviour inside the method by inspecting the HttpServletRequest:
#RequestMapping(value = "/add/email", method = RequestMethod.POST, )
public String addNewAccountEmail(#Valid #ModelAttribute EmailClass emailObject, BindingResult bindingResult, Model model, HttpServletRequest request) {
if (request.getParameter("form1_param") != null) { // identifies 1st form
// Do something
} else if (request.getParameter("form2_param") != null) { // indentifies 2nd form
// Do something else
}
...
}
It would be cleaner however to have multiple controller methods mapped to the same path, but specify different params in the RequestMapping - to differentiate the different forms.
#RequestMapping(value = "/add/email", params="form1_param", method = RequestMethod.POST)
public String addNewAccountEmail1(#Valid #ModelAttribute EmailClass emailObject, BindingResult bindingResult, Model model) {
// Do something specific for form1
return displayForm(model);
}
And also:
#RequestMapping(value = "/add/email", params="form2_param", method = RequestMethod.POST)
public String addNewAccountEmail2(#Valid #ModelAttribute EmailClass emailObject, BindingResult bindingResult, Model model) {
// Do something specific for form2
return displayForm(model);
}
Etc.

#RequestMapping accepts arrays as parameters (with an or semantic).
#RequestMapping(
value = "/add/email",
method = { RequestMethod.POST, RequestMethod.PUT } )

Related

Is there a way, in a spring controller, to add objects to a Freemarker Model without #ModelAttribute

I have my MVC view resolver set to Freemarker as normal. But I want to add a bunch of objects to my model.
Now I know I can do something like this:
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String add(#ModelAttribute("user") User user) {
and that will map parameters and create a User object that gets added to the template marker. and I know I can do this:
#RequestMapping(value = "/index", method = RequestMethod.GET)
public String index(#ModelAttribute("model") ModelMap model) {
Where I can add just about everything I want. But my question is do I have to do it that way?
I am wondering if there is a way to do something like this:
#RequestMapping(value = "/index", method = RequestMethod.GET)
public String index(HttpServletRequest req) {
MyContext myContext = new MyContext();
myContext.addStuff(stuff);
.... add more stuff
MagicViewObject.addModel(myContext);
return "freemarkerTemplate"
}
And then have access to the myContext object in the freemarker Template. Now I know I can probably do this with the #ModelAttribute("model") ModelMap model, but my question is: is there another way to do it. I don't like annotations in method signatures. I'm weird that way.
Return an org.springframework.web.servlet.ModelAndView object.

Spring data binding with and without #modelAttribute in method parameter

I've read that adding #modelAttribute in method param binds the incoming data to the object and add it to the model object as attribute.
#RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(#ModelAttribute User user) {
return "list";
}
if this is accessed via /list?name=unnamed, in list.jsp "unnamed" can be seen using {user.name} because it was added as model attribute for list.jsp. This is very clear to me.
But if i do
#RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(User user) {
return "list";
}
"unnamed" can still be seen using {user.name} when accessed via /list?name=unnamed. I thought the user object will not be added into model because it does not have #ModelAttribute annotation.
Are you sure of this part of code: return "list"; ???
Seems you are returnig the String, not the object. Try it and post and both cases, I have no permissions to comment yet =\

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 binding multiple attributes to same #ModelAttribute

I have a preview page which takes add or edit models and displays the preview.
#RequestMapping(value = "/preview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("editForm") FormModel editFormModel) {
//action
}
#RequestMapping(value = "/preview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("addForm") FormModel addFormModel) {
//action
}
I need to call preview from add form page and edit form page. The models I'm going to pass are same but come from different forms.
(1) Is there a way ModelAttribute supports this kind of multi-attribute name mapping?
(2) How can I think about redesigning this? Thinking about (a) Renaming the form name/attribute before form submit to use the same attribute name. (b) Remove ModelAttribute altogether - That's not an option for me as I'm using spring mvc form binding.
Note: I'm using editForm/addForm as session attributes.
Not actually solutions but work arounds.
Approach 1:
#RequestMapping(value = "/preview", method = RequestMethod.PUT)
public ModelAndView preview(#ModelAttribute("editForm") FormModel editFormModel) {
//action
}
#RequestMapping(value = "/preview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("addForm") FormModel addFormModel) {
//action
}
Approach 2:
#RequestMapping(value = "/editpreview", method = RequestMethod.PUT)
public ModelAndView preview(#ModelAttribute("editForm") FormModel editFormModel) {
//action
}
#RequestMapping(value = "/addpreview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("addForm") FormModel addFormModel) {
//action
}

Spring pass model from POST

I'm quite knowledgeable with Spring yet. I'm trying to learn. Please help me quite a bit.
I have a form which uses modelAttribute="projectBean" which is working perfectly fine and I'm able to manipulate data on the controller below
#RequestMapping( value = "projects/newProject", method = RequestMethod.POST )
public String newProject( #ModelAttribute( "projectBean" )
ProjectBean projectBean, HttpServletRequest request, ModelMap model )
{
model.addAttribute( "projectBean", projectBean );
return "redirect:../projects/projectItems.do";
}
I'm done saving it to the database so I want now to pass the projectBean to another controller
#RequestMapping( value = "/projects/projectItems", method = RequestMethod.GET )
public String projectItems( #RequestParam( defaultValue = "" )
String message, #RequestParam( defaultValue = "" )
String messageType, #RequestParam( defaultValue = "" )
String projectID, HttpServletRequest request, #RequestParam( "projectBean" )
ProjectBean projectBean, ModelMap model )
{
return "project/items";
}
But i'm having this exception: Required ProjectBean parameter 'projectBean' is not present
What am I doing wrong?
You don't generally pass a model from one Controller to another Controller. I assume you are trying to perform some logic before passing the model to another JSP page (project/items in this case).
You can achieve the same in newProject() controller rather than trying to pass the model to another Controller
#RequestMapping( value = "projects/newProject", method = RequestMethod.POST )
public String newProject( #ModelAttribute( "projectBean" )
ProjectBean projectBean, HttpServletRequest request, RedirectAttributes redirectAttributes)
{
//Call DAO class to save the model to database
//Call BusinessDelegate class to perform the additional logic
//Add beans to RedirectAttributes using addFlashAttribute() methods to make it available in next JSP page
redirectAttributes.addFlashAttribute("projectBean", projectBean);
return "redirect:/project/items";
}
Note: In order to apply POST-REDIRECT-GET pattern, you should use RedirectAttributes instead of ModelMap to make the model attributes available in redirected JSP page.

Resources