I've got some code which looks like this
@Controller
public class FooController {
@RequestMapping(value = "/foos", method = POST)
public String createFoo(@ModelAttribute("FooDto")
@Valid FooDto fooDto, BindingResult bindingResult, Model model) {
var foo = fooMapper.toFooModel(fooDto);
if (bindingResult.hasErrors()) {
var fooDto1 = fooMapper.toFooDto(foo);
model.addAttribute("FooDto", fooDto1); // binding result disappears
...
}
...
Also some Test for my Controller class which looks like the following:
@Test
void createFooWithValidationError() throws Exception {
perform(post("/foos")).andExpect(status().isOk())
.andExpect(model().attribute("foo", notNullValue()))
.andExpect(model().errorCount(1)); // this fails, no error/binding result exists!
}
The test has not been successfull, because there is no binding result after setting
model.addAttribute("FooDto", fooDto1);
.
I know, that i could set
model.addAttribute("FooDto", fooDto);
whereby the binding result does not disappear.
However I wonder why the binding result is disappearing. Is there some spring magic which binds the binding result to the concrete instance of my model attribute? Is there some other workaround to make the above code working?
CodePudding user response:
I'll dived into the spring code and found my answer:
model.addAttribute("FooDto", fooDto1);
does call
public ModelMap addAttribute(String attributeName, @Nullable Object attributeValue) {
Assert.notNull(attributeName, "Model attribute name must not be null");
put(attributeName, attributeValue); // calls BindingAwareModelMap
return this;
}
public class BindingAwareModelMap {
public Object put(String key, @Nullable Object value) {
removeBindingResultIfNecessary(key, value);
return super.put(key, value);
}
private void removeBindingResultIfNecessary(Object key, @Nullable Object value) {
...
if (bindingResult != null && bindingResult.getTarget() != value) {
remove(bindingResultKey); // Removes the binding result if target refernces to other instance
}
}
}
As u can see if the binding results references to an other instance the binding result entry in ModelMap
is removed.