Spring binding multiple attributes to same #ModelAttribute - spring

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
}

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.

Avoid Spring MVC form resubmission when refreshing the page

I am using spring MVC to save the data into database. Problem is it's resubmitting the JSP page when I am refreshing the page.
Below is my code snippet
<c:url var="addNumbers" value="/addNumbers" ></c:url>
<form:form action="${addNumbers}" commandName="AddNumber" id="form1">
</<form:form>
#RequestMapping(value = "/addNumbers", method = RequestMethod.POST)
public String addCategory(#ModelAttribute("addnum") AddNumber num){
this.numSrevice.AddNumbers(num);
return "number";
}
You have to implement Post/Redirect/Get.
Once the POST method is completed instead of returning a view name send a redirect request using "redirect:<pageurl>".
#RequestMapping(value = "/addNumbers", method = RequestMethod.POST)
public String addCategory(#ModelAttribute("addnum") AddNumber num){
this.numSrevice.AddNumbers(num);
return "redirect:/number";
}
And and have a method with method = RequestMethod.GET there return the view name.
#RequestMapping(value = "/number", method = RequestMethod.GET)
public String category(){
return "number";
}
So the post method will give a redirect response to the browser then the browser will fetch the redirect url using get method since resubmission is avoided
Note: I'm assuming that you don't have any #RequestMapping at controller level. If so append that mapping before /numbers in redirect:/numbers
You can return a RedirectView from the handler method, initialized with the URL:
#RequestMapping(value = "/addNumbers", method = RequestMethod.POST)
public View addCategory(#ModelAttribute("addnum") AddNumber num,
HttpServletRequest request){
this.numSrevice.AddNumbers(num);
String contextPath = request.getContextPath();
return new RedirectView(contextPath + "/number");
}
My answer shows how to do this, including validation error messages.
Another option is to use Spring Web Flow, which can do this automatically for you.

redirect from get method to post method in spring controller

I have a link retailerId after clicking the link control will go to the below controller:
#Controller
#RequestMapping(value = "/auth/adminsearchowner")
public class AdminSearchOwnerController {
#RequestMapping(value = "/retailerId/{retailerId}",method = RequestMethod.GET)
public ModelAndView viewRetailerInfo(
#PathVariable("retailerId") String retailerId,
#ModelAttribute EditRetailerLifeCycleBean editLicenseBean) {
editLicenseBean.setSelectedIDsString(retailerId);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("editLicenseBean",editLicenseBean);
modelAndView.setViewName("redirect:/auth/adminlicense/viewlicense");
return modelAndView;
}
}
where /auth/adminlicense/viewlicense is in another controller and we have both GET and POST method for this /auth/adminlicense/viewlicense request mapping. I want to call the post method from the earlier controller.
#Controller
#RequestMapping("/auth/adminlicense")
public class AdminViewLicenseController {
#RequestMapping(value = "/viewlicense", method = RequestMethod.GET)
public ModelAndView searchRetailerLicense(
#ModelAttribute("editLicenseBean") EditRetailerLifeCycleBean editLicenseBean,
HttpSession session) {
}
#RequestMapping(value = "/viewlicense", method = RequestMethod.POST)
public ModelAndView getLicenseDetails(
#ModelAttribute EditRetailerLifeCycleBean lifeCycleBean,
HttpSession session) {
}
}
but it is going to GET method. Could you tell me the solution?
There is no solution. A redirect cannot cause a POST to be sent by the browser.
Rethink your design.
Try:
modelAndView.setViewName("forward:/auth/adminlicense/viewlicense");
instead of
modelAndView.setViewName("redirect:/auth/adminlicense/viewlicense");
A design in which you are trying to send some data from one controller(server-side) to another(server-side) via user's browser(client-side) is probably not the best idea, anyway.
Hope it helps!

Spring 3 : Binding same controller method to multiple form action

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 } )

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