I'm looking for a way to access a BindingResult from within the view (in my case a JSP page).
I have the following controller method:
#RequestMapping(value="/register/accept.html",method=RequestMethod.POST)
public ModelAndView doRegisterAccept(
#Valid #ModelAttribute("registrationData") RegistrationData registrationData,
BindingResult bindingResult
) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("bindingResultXXX", bindingResult);
modelAndView.setViewName("users/registration/register");
return modelAndView;
}
When executing this, the RegistrationData get's populated and the errors are stored in the BindingResult. So far, so good. However, I do have to add the BindingResult manually into the ModelAndView for it to become visible within the View.
Is there a way to automatically add the BindingResult into the model? I already tried adjusting the method signature to
public ModelAndView doRegisterAccept(
#Valid #ModelAttribute("registrationData") RegistrationData registrationData,
#ModelAttribute("bindingResult") BindingResult bindingResult
) {
where I was hoping - as any other parameter - the BindingResult would get added into the model under the key bindingResultbut unfortunately that's not working.
So, any ideas?
Addition
I just found that the results indeed get published using the key
org.springframework.validation.BindingResult.<NAME_OF_MODEL_ATTRIBUTE>
so I suppose just adding it under the plain name is not encouraged by the Spring guys?!
You have to add "errors" tag in your form view, as described here : http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/view.html#view-jsp-formtaglib-errorstag.
Then in your Controller, you just put a test :
if(bindingResult.hasErrors()) {
... display your form page again
(and "errors" tags will be populated by validation messages corresponding to each field) ...
}
else {
... here, you know that your RegistrationData is valid so you can treat it (save it I guess) ...
}
Related
I have and endpoint, where I validate received json document which contains collections of objects. I would like to only log these objects which don't pass a validation, when others i would like to store in db. Controller should return 200 OK in that situation. I was trying to use BindingResult object for this purpose. Unfortunately i always get a ConstraintViolationException. It seems that it validates it before it enter the method and throw exception. How can I force it to use BindingResult object ?
#RestController
#Validated
#RequestMapping(path = "/test")
class TestController {
#PostMapping(consumes = APPLICATION_JSON_VALUE)
public ResponseEntity<Void> addObjects(#RequestBody #Valid List<Document> objects, BindingResult bindingResult) {
if(bindingResult.hasErrors()){
}
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
I'm using Spring Boot 1.5.9.RELEASE with Java 8
I've managed to solve it finally. Problem is with #Validated annotation on controller class. With this annotation spring do a validation on request and throw ConstraintViolationException. Without that, validation is triggered later and it results are stored in BindingResult object as expected
Could You please add the model classes with its annotations?
Remember that if You have any fields in Document class which are Your custom defined classes and You want it to be validated also then You have to decorate these fields with #Valid annotation too.
First of all, I know that there are similar questions regarding "Preserving model state with Post/Redirect/Get pattern", but none of these have my very specific problem:
background:
My Code is working in an enterprise CMS software which does a lot of things. One of them is URL rewriting: Whenever I generate Links to my controller, dependending on the environment, the links are shortened - That's a SEO thing and can't be discussed.
I.e. if my Controller URL is /webapp/servlet/myController/doSomething, the generated URL will be /myController/doSomething. There's some LinkProcessing functionality that we have to use.
An apache rewrite rule will then expand this short url to /webapp/servlet/myController/doSomething when the apache uses mod_rewrite to call the corresponsing code on the tomcat:
RewriteCond %{REQUEST_URI} ^/myController/(.*)
RewriteRule ^/myController/(.*) /webapp/servlet/myController/$1 [PT,L]
Problem:
I'm trying to implement the Post/Redirect/Get pattern using Spring 3.1.2. I'm generating a form and POST it to the Controller, which validated and makes a redirect to either the success or error page using GET (Post/Redirect/Get pattern).
(highly simplified) Code:
#RequestMapping()
public class MyController {
#RequestMapping(value = "/doDispatch", method = RequestMethod.POST)
public RedirectView handleDispatch(RedirectAttributes redirectAttributes,
#Validated #ModelAttribute FormBean formBean,
BindingResult binding) {
if (binding.hasErrors()) {
redirectAttributes.addFlashAttribute(formBean);
redirectAttributes.addFlashAttribute(BindingResult.MODEL_KEY_PREFIX+"formBean", binding);
return new RedirectView(generateLink("/error"));
} else {
redirectAttributes.addFlashAttribute(formBean);
redirectAttributes.addFlashAttribute(BindingResult.MODEL_KEY_PREFIX+"formBean", binding);
return new RedirectView(generateLink("/success"));
}
}
#RequestMapping(value = "/success")
public ModelAndView handleSuccess(#ModelAttribute FormBean formBean) {
// do stuff (save things in the DB)
// ...
final ModelAndView modelAndView = createModelAndView(formBean);
modelAndView.addObject("success", true);
return modelAndView;
}
#RequestMapping(value = "/error")
public ModelAndView handleError(#Validated #ModelAttribute FormBean formBean,
BindingResult binding) {
final ModelAndView modelAndView = createModelAndView(formBean);
modelAndView.addObject("binding", binding);
modelAndView.addObject("formBean", formBean);
return modelAndView;
}
}
The problem ist that this generateLink() method will either generate links starting with /webapp/servlet or not - depending on the environment/success. And that's how this whole Enterprice CMS thing works. (that's the part which cannot be discussed)
Spring Flash-Attributes on the other hand work hand in hand with the URLs that are returned and store the URL as part of the FlashMap:
Quote from http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-flash-attributes :
To reduce the possibility of such issues, RedirectView automatically "stamps" FlashMap instances with the path and query parameters of the target redirect URL. In turn the default FlashMapManager matches that information to incoming requests when looking up the "input" FlashMap.
Since the next request (let's say I've had an error and returned "/myController/error") will be expanded to /webapp/servlet/myController/error, the FlashMap will not apply to this request, since the URLs do not match.
The code that is responsible is this here (AbstractFlashMapManager.java:157 ff):
protected boolean isFlashMapForRequest(FlashMap flashMap, HttpServletRequest request) {
if (flashMap.getTargetRequestPath() != null) {
String requestUri = this.urlPathHelper.getOriginatingRequestUri(request);
if (!requestUri.equals(flashMap.getTargetRequestPath())
&& !requestUri.equals(flashMap.getTargetRequestPath() + "/")) {
return false;
}
}
// ...
}
Question:
Do you know a way how I can still generate short URLs on the one hand, but pass the FlashAttributes to the following GET request?
Best regards and thanks for your help in advance,
Alexander
The best solution I've found so far is using an Interceptor that updates the targetRequestPath by prefixing /<webappPath>/<servletPath> to those FlashMaps from RequestContextUtils.getOutputFlashMap(request) that are missing this information. BTW: this can only be done in the afterCompletion method, because otherwise the targetRequestPath won't be set at all, when using a RedirectView.
Any other, better solutions?
i need to pass an object into a page that i'm going to redirect
#RequestMapping(value = "/createNewQuest",method={ RequestMethod.GET, RequestMethod.POST })
public ModelAndView createNewQuest(Quest quest,ModelAndView mav) {
questService.addQuest(quest);
mav.addObject("quest", quest);
mav.setViewName("redirect:/control/displayQuest");
return mav;
}
my controller class seems like this but displayQuest page not getting the quest object?
any help wil be greatly appreciabl..
Spring added Flash Attributes to handle this scenario:
Annotated controllers typically do not need to work with FlashMap
directly. Instead an #RequestMapping method can accept an argument of
type RedirectAttributes and use it to add flash attributes for a
redirect scenario. Flash attributes added via RedirectAttributes are
automatically propagated to the "output" FlashMap. Similarly after the
redirect attributes from the "input" FlashMap are automatically added
to the Model of the controller serving the target URL.
Example
#RequestMapping(value = "/createNewQuest",method={ RequestMethod.GET, RequestMethod.POST })
public ModelAndView createNewQuest(#ModelAttribute("quest") Quest quest,
BindingResult binding, ModelAndView mav,
final RedirectAttributes redirectAttributes) {
questService.addQuest(quest);
redirectAttributes.addFlashAttribute("quest", quest);
mav.setViewName("redirect:/control/displayQuest");
return mav;
}
if you are using spring form tag , then your form tag should look like below
<form:form name="yourformname" id="formid" commandName="quest">
then you controller model object "quest" will be mapped to spring form tag commandName variable, later you can access the variable
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.
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.