Spring BindingResult in MVC Controller method - java

Take this part of code for example
#RequestMapping(method=RequestMethod.POST)
public String addUserFromForm(#Valid User user, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "users/edit";
}
userService.saveUser(user);
return "redirect:/users/"+user.getName();
}
How does the controller's method knows about BindingResult object? Where's that object initialized and passed to the method?

All the parameters passed to controller methods are initialized and filled by the spring-mvc dispatcher servlet.
The User object is instantiated and mapped to request parameters by a binder (DataBinder implementation). If there are problems, the BindingResult is filled with information about these problems and passed to your method.

Related

Spring MVC: Passing a Model as an argument to a controller method VS instantiating it explicitly

I have created two test methods in my MVC Controller class. In the first method the Model is being passed as an argument and in the second one I instantiate it directly. In both methods I add a single attribute to the Model instance:
#RequestMapping("/test-modelParam")
public String testMethod1(Model model) {
model.addAttribute("testname", "testvalue");
return "/testview";
}
#RequestMapping("/test-modelInstantiatedExplicitly")
public ModelAndView testMethod2() {
ModelAndView mav = new ModelAndView("/testview");
mav.addObject("testname", "testvalue");
return mav;
}
The View gets populated correctly in both cases.
Is there any difference between the two approaches? If so, Where is it preferred to use one over the other?
In the end there is no difference everything will end up in a ModelAndView eventually.
When using the Model or ModelMap as a method argument it will get pre-populated with some values
Path Variables
Result of any #ModelAttribute annotated methods
Any #SessionAttributes available to the controller
In short it is the pre-populated model made available to the method.
The Model is always created and is merged with the one you add/create with a ModelAndView.

Is it ok to use a ModelAttribute as a method parameter for RequestMapping method?

I have been using the following code:
#RequestMapping(value="/myUrl", method=RequestMethod.GET)
public ModelAndView myRequestHandler(
HttpServletRequest request, HttpServletResponse response,
#ModelAttribute(value="paramName") #ValidMyModelForm form)
// automatically populates form setters from form:form in JSP view
{
}
Reading the answers at the following link I am starting to doubt that my usage of ModelAttribute is not correct here.
What is #ModelAttribute in Spring MVC?
Am I using it the right way? It seems to work but want to make sure I am not doing anything wrong.
The form object is added to model in a separate method using code that looks like:
modelAndView.addObject("formName", new MyModelForm());
In the JSP view I have a the forms name added as the commandName="formName".
This signature also should have worked perfectly for you:
public ModelAndView myRequestHandler(#Valid MyModelForm form)
here Spring MVC will ensure that a MyModelForm instance is created and bound based on what is submitted from your form.
Now, what does additional #ModelAttribute bring you:
First the simple case:
public ModelAndView myRequestHandler(#ModelAttribute("paramName") #Valid MyModelForm form)
Assuming you do not have any other method with #ModelAttribute, what the above will do is look for a model by name paramName, which is not likely to be present because of the assumption, then an instance of MyModelForm will be created and bound just like before, with one addition that you now have a model object with name paramName available that you can use in your view:
paramName.myformValue etc..
So in essence:
public ModelAndView myRequestHandler(#ModelAttribute("paramName") #Valid MyModelForm form)
is equivalent to:
public ModelAndView myRequestHandler(#Valid MyModelForm form, Model model) {
...
model.addAttribute("paramName", form)
}
Secondly, if you had a method annotated with #ModelAttribute which preloads say part of your MyModelForm:
#ModelAttribute("paramName");
public MyModelForm loadModel(int id) {
MyModelForm fromDB = loadFromDB(id);
}
Now, the advantage of your method signature:
public ModelAndView myRequestHandler(#ModelAttribute("paramName") #Valid MyModelForm form)
will be that the model that you have pre-populated from your DB, will be enhanced with what is submitted in the form.

Spring MVC and Request Attributes

I think the original question was confusing.
I have a HashMap that needs to be a Collection from a database that I'd like to send to a view via a Spring Controller. I don't want to put this HashMap in the model.addAttribute() because the Spring Model object returns a Map and my JSP needs the collection to be a Collection<Object>. If I set my HashMap.values() in a request.setAttribute, how do I go about dispatching that request variable to the view if my method is returning a String?
#RequestMapping(method = RequestMethod.GET)
public String home(Locale locale, Model model, HttpServletRequest request) {
model.addAttribute("surveys", mySurveys); //this is a map and I need a Collection<Object>
//So I'd like to do this, but how do I get to the "evaluations" object in a view if I'm not dispatching it (like below)??
request.setAttribute("evaluations", mySurveys);
//RequestDispatcher rd = request.getRequestDispatcher("pathToResource");
//rd.forward(request, response);
return "home";
}
EDIT: The Spring Tag library cannot be used for this particular usecase.
Thanks.
If mySurveys is a Map, then perhaps you can put mySurveys.values() into the ModelMap instead of mySurveys (Also, are you intending to use a ModelMap instead of a Model?)
In the code below, surveys would be a Collection of Objects and would be accessible in the jsp via ${surveys}
#RequestMapping(method = RequestMethod.GET)
public String home(ModelMap modelMap, HttpServletRequest request) {
Map<String,Object> mySurveys = getMySurveys();
modelMap.addAttribute("surveys", mySurveys.values());
return "home";
}
I think you're confused as to what ModelMap is.
You can annotate whatever variable you want to access in the view by #ModelAttribute and Spring will automatically instantiate it, and add it to the ModelMap. In the view, you can use it like:
<form:form modelattribute="myAttribute">
<form:input path="fieldInAttribute">
</form:form>
Hope this answers your question

Validating #RequestParam in Spring 3 MVC

How do I validate Spring #RequestParam so that they are in BindingResult with out having to use some sort of POJO transfer object (#ModelAttribute)?
I could use MapBindingResult and put the request parameters in that but then I have to get that binding result into the model.
Which I can do with org.springframework.validation.BindingResult.MODEL_KEY_PREFIX + name.
Is there a better way to bind and validate request parameters (instead of making another POJO)?
If you are using Spring MVC 3.0 or later, you can use the declarative validation support Spring provides. You would then declare the validation restrictions on the model bean and add the #Valid annotation to your form backing bean as described in the chapter "Spring Validation" in the reference docs.
If you add a BindingResult parameter directly after the bean being validated, you can then check for validation errors in your controller:
// .. in the bean class
public class MyBean {
#NotNull
private String name;
// ..
}
// .. in the #Controller
public ModelAndView doSomething(#Valid MyBean data, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
// back to form
}
// do something with the bean
}

#ModelAttribute in a method

Imagine a code like this one:
#RequestMapping(value="/users", method=RequestMethod.GET)
public String list(Model model) {
...
}
#InitBinder("user")
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields("password"); // Don't allow user to override the value
}
#ModelAttribute("user")
public User prepareUser(#RequestParam("username") String username){
...
}
#RequestMapping(value="/user/save", method=RequestMethod.POST)
public String save(#ModelAttribute("user") User user, Model model) {
...
}
I use an init binder to avoid that a field can be binded and I mark a method (prepareUser()) with #ModelAttribute to prepare my User object before it is binded. So when I invoke /user/save initBinder() and prepareUser() are executed.
I have set "user" in both #InitBinder and #ModelAttribute so Spring-MVC could understand that this methods should only be applied before executing a method with #ModelAttribute("user").
The problem is that the method annotated with #ModelAttribute("user") is executed before every mapped method of this controller. For example if I invoke /users prepareUser is executed before list() method. How can I make that this preparer is only executed before save() method having all the methods in the same controller?
Thanks
That's not really what #ModelAttribute is for. If you use it as a method parameter, it puts the annotated parameter into the model (that's fine). If you put it on a method, it's called every time to provide reference data that every method in the controller should have access to.
If you want to take control of building up your User object, you have several options. The two that are most obvious to me are:
Use your InitBinder method to add a new custom editor (a PropertyEditor class) for building User objects,
Use the conversion service in Spring 3 to convert string usernames to User objects.

Categories

Resources