Why didn't initbinder call on submit form? - spring

I have a form, but when I submit it, my initbinder doesn't intercept my post request.
This is my initbinder:
#InitBinder(value="confermaDto")
protected void initBinderDto(final WebDataBinder binder, final Locale locale) {
binder.registerCustomEditor(MyClass.class, myClassEditor);
}
And this is my method to intercept the post:
#RequestMapping(value="confermaDati", method = RequestMethod.POST)
public String confermaDati(#Valid final ConfermaDatiAttrezzaturaDto confermaDto,
final BindingResult bindingResult, final Model uiModel, final HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
uiModel.addAttribute("message", "Errore Salvataggio");
uiModel.addAttribute("dto", confermaDto);
}
uiModel.asMap().clear();
return "redirect:/";
}
I think, that it should work because the value in initbinder, and the name of my model attribure are equal.
So why don't it work??
Thank you

The names of command/form attributes and/or request parameters that this init-binder method is supposed to apply to.
Default is to apply to all command/form attributes and all request parameters processed by the annotated handler class. Specifying model attribute names or request parameter names here restricts the init-binder method to those specific attributes/parameters, with different init-binder methods typically applying to different groups of attributes or parameters.
Above is from the javadoc of #InitBinder.
In your code you specify the name of a model attribute to use, namely confermaDto. However in your request handling method there is no notion of a model attribute (i.e. nothing is annotated with #ModelAttribute).
public String confermaDati(#Valid final ConfermaDatiAttrezzaturaDto confermaDto, final BindingResult bindingResult, final Model uiModel, final HttpServletRequest httpServletRequest) { ... }
You have a argument annotated with #Valid which will only trigger validation, Spring will also instantiate this object and put values from the request onto it however it is NOT designated as a model attribute. Next to your #Valid annotation add the #ModelAttribute annotation. (Or remove the name from the #InitBinder annotation so that it will always be applied).
I think, that it should work because the value in initbinder, and the name of my model attribute are equal. So why don't it work??
In short to answer this question, the method argument name is equal but there is no model attribute. Hence no triggering of the #InitBinder method.

If you do not specify a ModelAttribute value into RequestMapping annotated method You have to specify in value attribute of #Initbinder anotation the class required name with first letter NOT capitalized.

Related

Why we add #ModelAttribute("r") Reg reg in controller class?

#RequestMapping(value="/addpost",method=RequestMethod.POST)
public ModelAndView addpost(HttpServletRequest request,HttpServletResponse response,#ModelAttribute("r") Reg reg)
{
int id=r.id;
System.out.println(id);
return mv;
}
In this code,there is given #ModelAttribute("r") Reg reg.Wether this modelattribute is taking values from jsp page?Can anyone explains the working of this ModelAttribute?
There are two usage of #ModelAttribute
At the Method Level
When the annotation is used at the method level it indicates the purpose of that method is to add one or more model attributes. Such methods support the same argument types as #RequestMapping methods but that cannot be mapped directly to requests.
#ModelAttribute
public void addAttributes(Model model) {
model.addAttribute("msg", "Welcome to the Netherlands!");
}
In the example, method adds an attribute named msg to all models defined in the controller class.
As a Method Argument
When used as a method argument, it indicates the argument should be retrieved from the model. When not present, it should be first instantiated and then added to the model and once present in the model, the arguments fields should be populated from all request parameters that have matching names.
In the code snippet that follows the employee model attribute is populated with data from a form submitted to the addEmployee endpoint.
#RequestMapping(value = "/addEmployee", method = RequestMethod.POST)
public String submit(#ModelAttribute("employee") Employee employee) {
// Code that uses the employee object
return "employeeView";
}
Refer : What is #ModelAttribute in Spring MVC?

SpringMVC Controller Method Doesn't Bring in Model

I noticed that when a Form Submit happens, my handler method that captures a #Model object as a parameter does have a valid Model available to me, e.g.
public ModelAndView save(final HttpServletRequest request,
#ModelAttribute(MODEL_NAME) MyModel model,
BindingResult bindingResult)
But when I have a handler method corresponding to a URL redirect from a simple <a href=".."> button/link, e.g.
<a href="myController.do&function=add">
leading to
#RequestMapping(params = "function=add")
public ModelAndView add(final HttpServletRequest request,
#ModelAttribute(MODEL_NAME) MyModel model) throws Exception
in that case, the model object is NULL. The model wasn't carried through the request and made available to me in the handler method.
Is there a way to reexpose the model? I do have the same param in both methods, but in the 2nd one the "model" object is NULL.
If you want to use GET request with you will need to add the model to the URL. Or you can use POST with a form instead.
See: how to pass a java object from jsp to spring controller on clicking a link

Spring MVC 3. How to modify the form after binding but before validation

I am developing an application using Spring MVC 3 annotated style controllers. In some ocassions I need to add/modify some field values depending on session variables or some other condition. To complicate things, the field may have a fixed value if some condition is matched, and read user input if not. The question is: There is a way to modify the form after binding but before validation using spring mvc 3? It was quite simple with SimpleFormController (onBind method), but I can't find a way in spring mvc 3.
An example:
a) I initialize the binder for my form. Add a validator, set the list of allowed fields, and add a list of generic property editors
#InitBinder(value = COMMAND_NAME)
#Override
public void initBinder(final WebDataBinder binder, final HttpServletRequest httpServletRequest) {
binder.setValidator(reenvioAsientoValidator);
binder.setAllowedFields(ReenvioAsientoForm.getListaCamposPermitidos());
.... Add some custom property editors for booleans, integers ....
}
b) Create model object
#ModelAttribute(value = COMMAND_NAME)
public ReenvioAsientoForm rellenaModelo(final HttpServletRequest httpServletRequest) {
final ReenvioAsientoForm form = new ReenvioAsientoForm();
... Add some field values, which cannot be modified by user ...
return form;
}
c) Binding happens: And it can modify any field that is in the allowedFields list. Even those I setted in phase b)
d) THIS IS WHAT I CAN'T DO. I need to set/modify some fields of the form. Can't be done in the create model phase, because those fields are in the allowedFields list (Depending on different conditions, they can be readonly or accept user input)
e) Validation happens
f) Controller POST method is invoked
#RequestMapping(value = URI_REENVIO_ASIENTO, method = RequestMethod.POST)
public ModelAndView submit(#Valid #ModelAttribute(COMMAND_NAME) final ReenvioAsientoForm form, final BindingResult result, HttpServletRequest request) {
.....
}
Somethings I've tried:
Modify inside validator, before validation: That is a possible solution, but I find it nasty, because I am using the validator for something it is not intended. Plus it only works if the form is validated.
Using a CustomPropertyEditor. This way I can check the condition and set the value during binding. The problem is that the binder is fired only if a the property is present in the request. If there were someway to fire it always, it would be a nice solution
The easiest workaround is to avoid using #Valid to trigger validation.
#Autowired
Validator validator;
#RequestMapping(value = URI_REENVIO_ASIENTO, method = RequestMethod.POST)
public ModelAndView submit(#ModelAttribute(COMMAND_NAME) final ReenvioAsientoForm form, final BindingResult result, HttpServletRequest request) {
// here comes the custom logic
// that will be executed after binding and before validation
// trigger validation
validator.validate(form, result);
// handle validation result and return view name
...
}
See related issue in Spring JIRA and the explanation that such hook / annotation won't be implemented - #MVC should provide an "onBind" hook prior to automatic validation.

Spring #InitBinder on #RequestBody

I'm trying to use the #InitBind annotation to map only certain fields on the object in the request body.
I have a spring controller defined in this way:
#RequestMapping(value = "addAddress", method = RequestMethod.POST)
public Object addAddressToPerson(
HttpServletRequest request,
HttpServletResponse res,
#RequestParam(value = "name", required = false) String name,
#RequestParam(value = "surname", required = false) String surname,
#RequestBody personDTO personJson,BindingResult result) {
The client request will be a a json representing a personDTO, but I don't want that field except the address to be mapped in the object for security reasons.
The input will be something like:
{ "address":"123 Street","........}
The personDTO contains many fields, and since spring map all of them directly in a DTO, that can be a problem.
I've seen that a solution is to use a Binder to declase the allowed or Disallowed field, but if I check the personDTO inside the controller, other fields are populate (for example if pass "id":"1234").
Any Hints?
The binder code is the following:
#InitBinder("orderJson")
protected void orderJsonBinder(WebDataBinder binder){
binder.setAllowedFields(new String[]{"address"});
}
Am I missing something?
Best Regards,
Luca.
But you are not binding request parameters to a model attribute bean, you are just asking spring to use an appropriate MessageConverter to convert the request body. As you say it is Json, you will use a MappingJackson2HttpMessageConverter (or MappingJacksonHttpMessageConverter with Jackson 1.x). The Spring Reference Manual says for this converter :
[This is an] HttpMessageConverter implementation that can read and write JSON using Jackson's ObjectMapper. JSON mapping can be customized as needed through the use of Jackson's provided annotations. When further control is needed, a custom ObjectMapper can be injected through the ObjectMapper property for cases where custom JSON serializers/deserializers need to be provided for specific types. By default this converter supports (application/json).
#InitBinder can only configure binding of #ModelAttribute annotated parameters. It is useless here. If Jackson annotations are not enough, you will have to use a custom object mapper.
And I am surprised that you can use a BindingResult after a #RequestBody parameter, because the documentation says that it should follow a #ModelAttribute one.

Spring - What order are the #modelattribute methods called in with multi-action controllers

I have a controller which has multiple actions and corresponding #modelattribute methods. One of the methods output is a input to the next method. However the value was never getting set. When I debugged, I found that the order in which the methods are called are not what I expected. Is it anyway related to the command name #ModelAttribute("nominationCommand") or the method name. What drives this?
#ModelAttribute("awardCommand")
public AwardCommand getAwardList(HttpServletRequest request, HttpSession session, Model model) {
#ModelAttribute("associateDetails")
public List<AssociateDetailsCommand> getAssociateList (HttpServletRequest request, HttpSession session, Model model) {
#ModelAttribute("achievementCommand")
public AchievementDetailsCommand getAchievementDetails(HttpServletRequest request, Model model) {
#ModelAttribute("departmentCommand")
public List<DepartmentCommand> getDepartmentList(HttpServletRequest request,HttpSession session, Model model) {
I need the methods to be called in the same order as listed above. But the third method is called, before the second one.
Need your help.
Spring is using Java Reflection for getting list of methods. In most cases it's quite unpredictable in terms of JDK.
The most common workaround is to replace #ModelAttribute with corresponding model in controller's handler.
#RequestParam("/example/view")
public ModelAndView view(HttpServletRequest request, Model model /*probably some more parameters*/){
model.put("awardCommand", getAwardCommand());
model.put("achievementCommand", getAchievementCommand());
///... TODO put other
}

Resources