I am using Spring MVC and in my controller, I want to be able to automatically bind incoming parameters to my Java object. It seems like this should be pretty easy to do. The only wrinkle is that the incoming parameter names (e.g. "username") may not match up exactly with the field name in the java object (e.g. "name").
From the Spring documentation (http://static.springsource.org/spring/docs/2.5.6/reference/mvc.html):
"Spring Web MVC allows you to use any
object as a command or form object....
All this means that you don't need to
duplicate your business objects'
properties as simple, untyped strings
in your form objects just to be able
to handle invalid submissions, or to
convert the Strings properly. Instead,
it is often preferable to bind
directly to your business objects. "
How do I actually do this? Any code or links appreciated.
For example, my business object
public class User {
private String username;
private String password;
//getters and setter
}
The request my controller is handling:
example.com/login?name=Steve&pw=1234
I would like to bind "Steve" to User.username and "1234" to User.password.
Thanks.
If I remember correctly, you can override public Object formBackingObject(HttpServletRequest request) and manually setup your command POJO.
In this case you can use custom argument resolver.
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
public Object resolveArgument(final MethodParameter parameter,
final ModelAndViewContainer container,
final NativeWebRequest webRequest,
final WebDataBinderFactory binderFactory) {
final User user = new User();
user.setUsername(webRequest.getParameter("name"));
user.setPassword(webRequest.getParameter("pw"));
return user;
}
}
And in your spring config:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="your.package.UserArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
example.com/login?username=Steven&password=1234
You can't expect spring MVC to do automatic binding when the property names don't match up. That's just not logical.
EDIT: Do what Kaleb said, overriding formBackingObject is cleaner.
I'll leave this note though:
However, if you have any control over both sides, I strongly recommend making the name consistent. Convention over configuration.
If you want to bind request to the POJO you may need to extend AbstractCommandController or if you have a form - SimpleFormController. I believe you will need the servlet version from org.springframework.web.servlet.mvc package.
use setCommandClass() to setup the correct type of your backing bean in controller constructor, ie POJO:
setCommandClass(User.class);
formBackingBean() used to create new instance of the POJO, that would be used by controller.
controller is responsible for mapping request parameters to the POJO fields.
if mapping is not working good for you, than override formBackingBean() or onBind() methods to read request paratemers and put values to POJO.
You should change your form or your object so that the names match up. If you're trying to use the features in a framework like Spring MVC, you should probably follow their conventions.
Convention over configuration.
Related
Thanks to #RestController I don't need to add annotation #ResposneBody, cause spring knows that it is rest controller, and he will not generate view, but instead it will return json object.
Unfortunately there is one more annotation related to this topic. It is #RequestBody, when controller method accept json object as a parameter. And it will have to be pointed before that parameter.
My question is there a way to get rid of that annotation (#RequestBody).? If my controller is rest controller (#RestController instead of regular #Controller) it should be demanded from spring?
No, you'll have to specify #RequestBody. A Java method can have only a single return value, and so the #ResponseBody is unambiguous, but there are multiple possible ways that mapped controller parameters might be interpreted (in particular, using #ModelAttribute with form encoding is a very common alternative to #RequestBody with JSON), and you'll need to tell Spring how to map the incoming request.
I am new to SpringMvc. Could anyone please explain the binding and working of spring form with the back end object in SpringMVC.
some of the doubts are
Consider the scenario, there is a form which will take user details and it will be persisted to db
1) I have seen a controller which creates User's instances and adding the attribute to ModelMap. What is the use of that?
#Controller
#RequestMapping("/form.html")
public ModelAndView form(ModelMap map){
User user= new User();
map.addAttribute("user",user);
return new ModelAndView("form","command",map);
}
2) What is the use of command here? in the form page, this 'user' object will be available?
*form.jsp
<form:form.... action="formprocess.html" commandName="user"/>
(If I want to use 'user' should it have been already created as above?)
3) Why do we use #ModelAttribute? Why do we use Model instead of ModelMap?
#Controller
#RequestMapping("/formprocess.html")
public String form(#ModelAttribute("user"User user,Model model){
model.addAttribute("username",user.getUserName());
return "formprocess";
}
could anyone please explain or provide a link which has sufficient explanations
Regarding ModelMap, model map is used to pass certain data from you controller to the view which you delegate from that controller. You add attributes from controller and later on get attributes from view page.
Regarding commandName, commandName="user" this is something that the controller uses to map the form fields to a particular bean or POJO fields. So you do not have to manually get all the request parameters and set it yo pojos when a form is submitted and controller receives the event.
Regarding #ModelAttribute, since you use #ModelAttribute("user") as method parameter, spring container will look for a a command name user from request object and map it's properties to the pojos defined in #ModelAttribute viz in your case User class.
Regarding difference between Model and ModelMap :
ModelMap subclasses LinkedHashMap, and provides some additional conveniences to make it a bit easier to use by controllers
addAttribute can be called with just a value, and the map key is then inferred from the type.
The addAttribute methods all return the ModelMap, so you can chain method called together, e.g. modelMap.addAttribute('x', x).addAttribute('y',y)
The addAttribute methods checks that the values aren't null
The generic type of ModelMap is fixed at Map<String, Object>, which is the only one that makes sense for a view model.
So nothing earth-shattering, but enough to make it a bit nicer than a raw Map. Spring will let you use either one.
You can also use the Model interface, which provides nothing other than the addAttribute methods, and is implemented by the ExtendedModelMap class which itself adds further conveniences.
I need to validate some simple forms in my application. In these forms I have one or two input text to validate so I'd like to not create a specific ModelAttribute class for every form. I'd like to use instead plain HTML form and use #RequestParam annotations to handle POST parameters.
Is there a way to use Spring form validation in this situation (without using model attribute) or should I implement a backing-form object and a validator for each form?
Currently it is not possible to use #Valid on individual #RequestParam, #PathVariable etc. to trigger validation. This is the relevant feature request on the Spring Issue Tracker. Let's cross our fingers for Spring 4.1!
In your case, you will either have to use #ModelAttribute, or perform custom validation inside the controller (or maybe a Spring interceptor if you want the same validation to apply to multiple endpoints)
I think you can do this with Annotation. You can specifie for your parameters annotation like :
#Size(min=3, max=5)
#NotNull
#NotEmpty
...
Without a model attribute, Spring form Validation is not possible. Because Spring Form Validation depends on Spring Form Binding, which is a linkage between form elements and Model Attribute. So how small the form may be, create a DTO(Model Attribute), bind it to form and Perform Validations.
Definitely not possible using Spring's validation API (Errors object):
java.lang.IllegalStateException: An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments to which they apply
You could instantiate a model object, fill it with the data from the plain form and validate that object programmatically.
I have a Spring MVC controller which is servicing GET requests, to perform a search.
These requests have many optional parameters which may be passed on the query string.
For example:
#Data
public class SimpleSearchRequest implements SearchRequest {
private String term;
private List<Status> stati;
#JsonDeserialize(using=DateRangeDeserializer.class)
private Range<DateTime> dateRange;
}
If I were using a POST or PUT for this, I could nicely marshall the inbound request using a #RequestBody. However, because I'm using a GET, this doesn't seem to fit.
Instead, it seems I'm required to list all the possible parameters on the method signature as #RequestParam(required=false).
Aside from leading to ugly method signatures, I'm also losing out on all sorts of OO goodness by not using classes here.
Attempting to use #RequestBody fails (understandably so), and as discussed here and here, using an actual request body on a GET is not desirable.
Is there a way to get Spring MVC to support marshalling multiple #RequestParam's to a strongly typed object on GET requests?
It seems the answer was to simply remove the annotation.
This worked:
#RequestMapping(method=RequestMethod.GET)
public #ResponseBody List<Result> search(SearchRequest request) {}
I am working on a wizard-like set of pages, where the user has to enter bits of data in several views for a final submission, allowing them to go back-and-forth prior to the final Submit is done. I was trying to use the same Bean, defined as a ModelAttribute, for all the views, basically just passing this one Bean around like a token in which each view adds its little bit of data.
The problem is that Spring MVC seems to create a new Bean on ever call. My admittedly fuzzy understanding about the Model was that it was basically like putting something into session, and that object would be around until the session was done. This does not seem to be the case.
So, I guess the first question is...where do Model Attributes "live", and for how long? Is there a better pattern out there for implementing a wizard-like interface using just Spring MVC (I am limited and can't use Web Flow...its not an approved tool where I work)?
It is NOT a good practise to use Model Attribute as a bean. It is good for manimulating form data before they are persisted into database.
#ModelAttribute("formAttribute") is created when you have specified it in your method as parameter:
public void getForm(#ModelAttribute("formAttribute") Form form) {
}
It is created before every method call by calling its contruct:
#ModelAttribute("formAttribute")
public Form getForm() {
return new Form();
}
When it is not specified in method parameter it doesn't exist.
There is possible to add your #ModelAttribute into session by defining #SessionAttributes on your controller:
#Controller
#SessionAttributes("formAttribute")
public HelloController
Then it is initialized once, when you firstly use it, and destroyed when you destroy it by calling:
public void finalStep(SessionStatus status) {
status.setComplete();
}
I think with combination of #SessionAttributes it is possible in relatively easy way create the wizard-like flow.
If Web-flow is not an option, you can try doing this:
Store your model attribute as a session attribute, this is accomplished by adding a #SessionAttribute annotation to your controller:
#Controller
#SessionAttribute("myconversationModel")
public class MyFlowController{
#RequestMapping
public String myMethod(#ModelAttribute("myconversationModel") ConversationModel myConversationModel){
....
}
}
Then where you think you are done with the flow, just accept an additional parameter SessionStatus and call sessionStatus.complete, this will wipe the attribute from session
#RequestMapping
public String myFinalMethod(#ModelAttribute("myconversationModel") ConversationModel myConversationModel, SessionStatus sessionStatus){
sessionStatus.complete();
....
}