Spring 3 MVC, multipart form and controller mapping issue - spring

My application uses submit name as action name consistently. It has worked so far.
Enter multipart form...
html
<form:form modelAttribute="screenObject" enctype="multipart/form-data">
<input name="save" value="Save" type="submit" />
Controller
public static final String ACTION_SAVE="save";
#RequestMapping(method=RequestMethod.POST, params=ACTION_SAVE)
public ModelAndView save(#ModelAttribute("screenObject") FileHeaderEditScreenObject screenObject, BindingResult bindingResult, Model model, Locale locale) {
Error
message Request method 'POST' not supported
If I remove enctype="multipart/form-data", control flows right into the save method. I do need different actions on this multipart form. I want to stay consitent so I hope I do not have to introduce any hidden fields to represent actions or submit the form to different urls...

I suggest to remove params=ACTION_SAVE from the annotation.
It look like that it doesn't parse submit action from a multipart request.
You can handle that parameter as a request parameter:
#RequestMapping(method=RequestMethod.POST, params=ACTION_SAVE)
public ModelAndView save(
#RequestParam(value = "submit", required = true) String action,
#ModelAttribute("screenObject") ....
{
switch (action) {
case "action1": ...
case "action2": ...
}
}
This is not very nice. I would just use different URLs like this:
#RequestMapping("/action1"})
public void action1(
#RequestMapping("/action2"})
public void action2(

Related

Spring is not showing validation error for Dynamically generated Form Id

Spring validation works fine for a form with static id. But in my scenario forms are generated dynamically on frontend.
For Example - My form bean is - GenericQuestionnaireForm so if i have the form element as below
<form:form method="post" modelAttribute="genericQuestionnaireForm" action="/save-generic-questionnaire">
validation works fine and the errors are correctly displayed.
But I have to capture the form values for different products so I am generating different forms with unique id by attaching the form name with the product number.
<form:form method="post" modelAttribute="product1_genericQuestionnaireForm" action="/save-generic-questionnaire">
when I submit this form to my controller method the BindingResult are getting attached to the genericQuestionnaireForm form object due to which the errors are not getting displayed on front end.
#RequestMapping(value = "/save-generic-questionnaire", method = RequestMethod.POST)
public String saveQuestionnaire(#Valid final GenericQuestionnaireForm genericQuestForm,final BindingResult bindingResult, final Model model,
final RedirectAttributes redirectModel, final HttpServletRequest request) throws CMSItemNotFoundException{
if(genericQuestForm != null) {
genericQuestionnaireFormValidator.validate(genericQuestForm, bindingResult);
if (bindingResult.hasErrors())
{ return //to the front end..}
}
My query is - Is there a way to attach the BindingResult to the dynamic form Id in order to show the generated errors. Or is there a better way to do form validation in this scenario?
<<form:form:form method="post" modelAttribute="genericQuestionnaireForm" action="/save-generic-questionnaire" >
<form:hidden path="id"/>
.................
</form:form>
modelAttribute attribute always remains the same.
#ModelAttribute("genericQuestionnaireForm")
public genericQuestionnaireForm getgenericQuestionnaireForm(){
return new genericQuestionnaireForm();
}
RequestMapping(value = "/save-generic-questionnaire", method =
RequestMethod.POST)
public String saveQuestionnaire(#Valid final GenericQuestionnaireForm genericQuestForm, BindingResult bindingResult.....
for identifing different products just use hidden id.

Why isn't my id preserved of the #ModelAttribute?

Why isn't my id preserved of the #ModelAttribute? Why do I have to send it as a hidden parameter in my form? Shouldn't spring handle this when using command?
#RequestMapping(value="/{supplierId}", method=RequestMethod.GET)
public String get(#PathVariable Long supplierId, Model model, Principal principal){
Form form = .... //Got a an existing form from DB
model.addAttribute("form", form);
return "/form";
}
#RequestMapping(value="/{supplierId}", method=RequestMethod.POST)
public String post(HttpServletRequest request, #PathVariable Long supplierId, #Valid #ModelAttribute("form") Form form, BindingResult result, Model model, Principal principal){
System.out.println(form.getID()); //Here the id is 0
safeFoodFormService.store(form, supplierId);
return "redirect:/supplier";
}
My Freemarker form
<form class="form-horizontal" action="<#spring.url "/forms/${supplier.ID?c}"/>" method="post" command="form">
It will work if I do add this lines inside my form
<#spring.bind "form.ID" />
<input type="hidden" name="${spring.status.expression}" value="${spring.status.value default("")}" />
I would be very nice if this could be handled by Spring. Thanks
Spring data binding takes the request name/value pair and binds the corresponding properties with same name to corresponding value. So if there is no id request parameter present in request, spring mvc has no way to map its value in the model bean which would be injected in to the controller method. So by any mechanism the form must have a input (hidden or otherwise) to let the spring bind its value to the bean property.

Spring redirect keeping parameters after POST

I am using spring 3.2.4 and I have a simple controller that takes a parameter, set it, and redirects the page. For some reason, it is keeping the param even after redirecting. i.e. The page starts at "/" and ends with "/?globalMessage=hereforever", I have tried clearing the modelMap, but that didn't work. I may be misunderstanding something along the lines. I am also adding the model to postHandle in HandlerInterceptor.
#RequestMapping(value = "globalMessage", method = RequestMethod.POST)
public String setGlobalMessage(#RequestParam String globalMessage) {
globalProperties.setProperty("globalMessage", globalMessage);
return "redirect:/";
}
Here is the jsp code in the front
<form method="post" action="/globalMessage">
<input name="globalMessage" type="text" name="message"/>
<input id="submitbutton" type="submit"/>
</form>
Spring's redirect view will automatically expose model attributes as URL parameters: https://jira.springsource.org/browse/SPR-1294. If the globalMessage is set in the model somewhere, then this will get appended to the redirect URL.
You can tell Spring not to do this using the setExposeModelAttributes method.
#RequestMapping(value = "globalMessage", method = RequestMethod.POST)
public String setGlobalMessage(#RequestParam String globalMessage) {
globalProperties.setProperty("globalMessage", globalMessage);
RedirectView redirect = new RedirectView("/");
redirect.setExposeModelAttributes(false);
return redirect;
}

Spring MVC binding extra objects

I'm getting some weird binding issue with Spring MVC 3.
My Controller request mapping looks like this:
#RequestMapping
public String save(HttpServletRequest req,
#ModelAttribute("userEditForm") UserEditForm form,
BindingResult formBindingResult,
ModelMap model,
#ModelAttribute("session") AdminSession session) {
// some validation etc
}
The UserEditForm:
public class UserEditForm {
private User user;
public User getUser() { ... }
public void setUser(User user) { ... }
}
The AdminSession:
public class AdminSession {
private User user;
public User getUser() { ... }
public void setUser() { ...}
}
What's happening is that when I submit my form, Spring is binding the User as I expect in my UserEditForm object, however, the AdminSession is also having it's User bound by Spring, in so far as it's property values are also updated.
I'm going to assume it's due to having a user property in both #ModelAttribute objects.
I thought that having the BindingResult after the UserEditForm form in the method signature would stop this? The objects are separate instances, and my form elements reference the UserEditForm object:
<#spring.bind "userEditForm.user.name" />
<input name="${spring.status.expression}" />
I've noticed that in the generated HTML it's outputting:
<input name="user.name" />
Hardcoding the name as userEditForm.user.name gives me errors, so that's not the way forward.
Is there anyway to stop this from happening?
That's the default behavior when you annotate a handler method parameter with the #ModelAttribute. Spring takes the request properties and matches them to properties of the objects annotated with #ModelAttribute. That's what Spring looks at when deciding what to do: your annotations.
Since both UserEditForm and AdminSession are annotated with #ModelAttribute and both have a User property, a request property named user.name will get bound to both User properties.
You tried to include the command name in the input name and got an error. That's because when binding occurs it occurs on your command object and Spring looks for properties on it (the bindinf path is relative to the command object) and off course the expression does not find any property with that name. If you want to use a full name you could wrap the form in another object and use that for your command instead, something like this:
public class UserEditFormWrapper {
private UserEditForm form;
public UserEditForm getForm() {
return form;
}
public void setForm(UserEditForm form) {
this.form = form;
}
}
Now you can use an expression like this in your inputs: form.user.name and when you submit to your handler method that now looks like this:
#RequestMapping
public String save(HttpServletRequest req,
#ModelAttribute("userEditForm") UserEditFormWrapper formWrapper,
BindingResult formBindingResult,
ModelMap model,
#ModelAttribute("session") AdminSession session) {
UserEditForm form = formWrapper.getForm();
// some validation etc
}
the binding won't be triggered since AdminSession does not have a form property.
That's one way to solve this but it's kind of a hack. You don't want to have the request parameters bound to AdminSession but that's part of your model so you must have created it somewhere and placed it on the model, right? If so, then remove it from the method's parameters and just get it from the model, something like:
#RequestMapping(value = "/test", method = { RequestMethod.POST })
public String handlePost(HttpServletRequest req,
#ModelAttribute("userEditForm") UserEditForm form,
BindingResult formBindingResult, ModelMap model) {
AdminSession session = (AdminSession) model.get("session");
// some validation etc
}

send an object via post

I want to make a form in which I can update my entity in my REST application. For example I have a User entity whith username and realname fields.
Do I need in my request method do something like this
#RequestMapping(value = "/admin/user/update", method = RequestMethod.POST)
public String updateHouse(#RequestBody String username, #RequestBody String realname, Model model)
??
I would prefer to make something like this
#RequestMapping(value = "/admin/house/update", method = RequestMethod.POST)
public String updateHouse(#RequestBody User user, Model model)
I mean that I want to send an object not every field. If i`ll have 20 fields in my entity I would have to add 20 params to my method. Thats not to fancy.
I`m using spring form tag
------- UPDATE
thanks for response. below diffrent entity but real case scenario that i`m trying to start
html code
<c:url var="houseUpdateLink" value="/admin/house/update" />
<form:form method="post" commandName="house" action="${houseUpdateLink}">
<form:input path="house.title" />
<input type="submit" value="send" />
</form:form>
controller method
#RequestMapping(value = "/admin/house/update", method = RequestMethod.POST)
public String updateHouse(#RequestBody House house, Model model) {
model.addAttribute("step", 3);
logger.info("test: " + house.getTitle());
return "houseAdmin";
}
and i receive
HTTP Status 415 -
type Status report
message
description The server refused this request because the request entity is in a format not supported by the requested resource for the requested method ().
You don't need #RequestBody here. Spring will automatically bind the form fields to the House class without it, i.e.
#RequestMapping(value = "/admin/house/update", method = RequestMethod.POST)
public String updateHouse(House house, Model model) {
In the Spring form tags, you can probably do user.username, and pass the User object as a param.

Resources