Spring MVC - Generic way of handling error and pass to view - java

From the thymeleaf guide, I have seen the following code snippet:
<ul th:if="${#fields.hasErrors('global')}">
<li th:each="err : ${#fields.errors('global')}" th:text="${err}">Input is incorrect</li>
</ul>
So, I m thinking if there is a way that I can pass error message from the Spring MVC controller and display the error somewhere on the page using the above code.
I have seen some online resources using BindingResult on the method signature as below.
bindingResult.rejectValue("username", "username.notvalid", "Username is not valid");
But what if the controller does not validate any objects? Will I still be able to use bindingResult?
I think I can also add error message as key pair into the model as below.
model.addAttributes("error","error message");
But, I want to know if there is some standard way of handling error in the Spring controller.
Should I use exception handling instead? But how can I pass error message back to the view?
Thanks
Updated use case:
For example, in method A, an error was detected as below.
public String methodA (Model m, RedirectAttributes model){
if (error == true){
model.addFlashAttribute("error.message","String");
return "redirect:MethodB";
}
}
public String methodB(Model m){
if(m.containsAttribute("error.message"){
//Way to pass the error to view page for display.
}
}
Should the error be handled in such way? The reason it goes through the second action is because there are certain data needed to be displayed on the final view page.

Do not use exceptions for validation errors, use BindingResult, give a look to this question and javadoc.
You can also create an implementation like MapBindingResult, use it and set it in the request/model for the view.
A BindingResult is a standard Spring data structure that can be used to hold errors, is usually used when there could be more than one error or if you have view code for it; if you just have one single error you could also just set it in the model.
Since you use flash attributes, in methodB the error is already in the model, so it will be available to the view. See also Spring MVC: Passing a Model as an argument to a controller method VS instantiating it explicitly
public String methodB(Model m){
...
return "myview";
}
Note that if you use a dot in attribute key (like error.message) you should use the right syntax in the view like, for JSP: model["error.message"] and not model.error.message

Related

Spring MVC Binding Command Object Using Get Request

I need to implement a controller that has a command object that is backing a filtering form for a search across multiple entries.
The problem is that the i was asked to do that without using POST request, instead using GET request only, and there before loosing the functionality of the default data binding that springs makes happily for us.
So i tried to implement a method, inside my controller, that looks like this:
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (isSearchRequest(request)) {
MyCommandObject myCommandObject = (MyCommandObject) getCommand(request);
System.out.println(managePositionsForm);
}
return super.handleRequestInternal(request, response);
}
But the getCommand returns me a brand new CommandObject with no values, despite that the values are present in the request object (i could retrieve then using the getParameter method of HttpServletRequest). But there isn't any binding.
So the question :
1) Is there any way to archive this?
2) Also is very important, that all the values in the form, are lost and, eventually (if this problem is solved) i will need to "persist" the filters for the users in order to avoid re entering after the first search.
Auto Response : setSessionForm(true); looks like can do the work! (According to javadoc)
Thanks to all!
Greetings
Victor.
Okey, i found a way to archive what a was looking for.
I will explain for the sake of those have the same problem before, and hoping to find a experienced user to validate this method... some quiet common is there a multiple ways to do a same thing and as human beings is very difficult to know without proper acknowledge the right path.. so this i a found looking inside the AbstractFormController (that is excellently documented with javadoc).
So what i did was the following, on my controller constructor i add these lines at the end :
setSessionForm(true);
setBindOnNewForm(true);
That all the magic!
But is not enought with setSessionForm(true). According to javadoc the setBindOnNewForm(boolean) method does the following :
/**
* Set if request parameters should be bound to the form object
* in case of a non-submitting request, i.e. a new form.
*/
So my guess are that these two flags are necessary to be marked as true, because :
The setSessionForm makes posible to store as a session attribute the form object, so "is stored in the session to keep the form object instance between requests, instead of creating a new one on each request" (according to javadoc of the setSessionForm method).
The setBindOnNewForm allows the population of the form object with the initial request (despites what type of request method we have). According the javadoc found the AbstractFormController "Only if bindOnNewForm is set to true, then ServletRequestDataBinder gets applied to populate the new form object with initial request parameters..."
But still i noticed, following the controller flow with a debugger, that the population is happening inside the method "getErrorsForNewForm(HttpServletRequest request)".. that is where a concrete object of type ServletRequestDataBinder is used IF the setBindOnNewForm is true, and later (as the javadoc stated) the onBindOnNewForm method is invoked, allowing the programmer to overwrite it with custom behavior, the default behavior is just empty (again this was double checked against the code of AbstractFormController).
I have an strong felling to validate my thoughts here, so if anyone can help me, that would be alright, besides the problem is solved!
Thanks to all in advance!
Greetings.

SpringMVC & #ModelAttribute peculiarities

Using Spring/SpringMVC 3.0.5 I've defined a method in my controller like this:
#RequestMapping(params = { "save","!delete" }, method = RequestMethod.POST)
public ModelAndView saveFoo(...
#ModelAttribute("vo") #Valid DescriptionBuilderVO vo, BindingResult result) {
...
result.rejectValue("foo.sequenceNumber", "foo.builder", new Object[]{vo.getFoo().getSequenceNumber()}, "Sequence Number too high"); vo.getFoo().setSequenceNumber(originalNumber);
return new ModelAndView(WebConstants.VIEW_BUILDER, "vo", vo);
Notice that I'm attempting to set a value in the VO object inside the controller. The funny thing is that if I do this with #ModelAttribute the new value doesn't show up. If I remove #ModelAttribute from the method contract, the new value appears exactly as you would think. The problem comes when there are errors, the only way to get the errors is to have the #ModelAttribute in the contract.
BTW my HTML looks like:
HTML
<form:input path="foo.sequenceNumber" id="sequenceNumber" size="4" maxlength="4"/>
<form:errors path="foo.sequenceNumber" cssClass="ui-state-error" />
foo.sequenceNumber = the value the user typed in; when I use #ModelAttribute
foo.sequenceNumber = the value I set in the controller; but I lose any errors
It seems to me that SpringMVC is putting the ModelAttribute VO into a "special" place and passing it back to the jsp but not in an obvious location. Does anyone know how I can get at the VO object in this situation?
wierd. The same thing works for me. The only difference i see is the order of Valid and ModelAttribute
can you try reversing the order of Valid and ModelAttribute?
public ModelAndView saveFoo(...
#Valid #ModelAttribute("vo") DescriptionBuilderVO vo, BindingResult result) {
}
BTW, which version of spring are you using?
I've tried many different things including the reordering the Valid and ModelAttribute annotations and it doesn't seem to make a difference.
In reading the documentation suggested by Pat in the comments it does refer to a special context where the VO is stored. Anyway, if you're trying anything similar to this I suggest you go about it in a different way perhaps building a completely new VO and passing it back to the view, which is what I did.
Try naming your object "descriptionBuilderVO", like the following:
#Valid #ModelAttribute("descriptionBuilderVO") DescriptionBuilderVO descriptionBuilderVO,
BindingResult result)
I know it shouldn't be this way, but I've found problems when the name of the object is different than the class name.
Note that you'll also have to change the name of the object in your jsp:
<form:form commandName="descriptionBuilderVO"
...etc...

Automatically return to input form on #Valid failure

I have the following method in my controller:
#RequestMapping(value = "/status", method = RequestMethod.POST)
public String update(#Valid StatusForm statusForm, BindingResult result) {
if(result.hasErrors()) {
return "statusForm";
}
//do stuff when there are no errors
return "dashboard";
}
It seems like in a lot of these methods there is a repeating code block:
if(result.hasErrors()) {
return "statusForm";
}
Is there a way to avoid having to insert this block and automatically redirect back to where the POST request came from in an event of Validation errors?
The same action can be accessed from multiple pages, so you would have to configure the error view somehow. In struts for example, you have an xml file describing the input jsp for each action. It would probably be possible to create an annotation containing the view id and implement a AnnotationMethodHandlerAdapter to handle this logic.
The current way of doiing this in code has one advantage. It allows you to to additional validations in your action method, maybe checking some values in a database or some more complicated business rules, and handling this errors in a uniform way.
In your controller handler method, make sure that the BindingResult argument is immediately after the command argument.

Spring - adding BindingResult to newly created model attribute

My task is - to create a model attribute by given request parameters, to validate it (in same method) and to give it whole to the View.
I was given this example code:
#Controller
class PromotionController {
#RequestMapping("promo")
public String showPromotion(#RequestParam String someRequestParam, Model model) {
//Create the model attribute by request parameters
Promotion promotion = Promotions.get(someRequestParam);
//Add the attribute to the model
model.addAttribute("promotion", promotion);
if (!promotion.validate()) {
BindingResult errors = new BeanPropertyBindingResult(promotion, "promotion");
errors.reject("promotion.invalid");
//TODO: This is the part I don't like
model.put(BindingResult.MODEL_KEY_PREFIX + "promotion", errors);
}
return
}
}
This thing sure works, but that part with creating key with MODEL_KEY_PREFIX and attribute name looks very hackish and not a Spring style to me. Is there a way to make the same thing prettier?
Skaffman answered the question but disappeared, so I will answer it for him.
The binding validation thing is there to bind and validate parameters, not arbitrary business objects.
That means, that if I need to do some custom validation of some general data that was not submitted by the user - I need to add some custom variable to hold that status and not use BindingResult.
This answers all the questions I had with BindingResult, as I thought it had to be used as a container for any kind of errors.
Again, thanks #Skaffman.

Seam/Hibernate validator listener

We use a standard SEAM setup here ... complete with the validation system that uses hibernate.
Basically what happens is a user enters a value into an html input and seam validates the value they entered using the hibernate validation.
Works fine for the most part except here's my problem: We need to record the results of validation on each field and I can't figure out a good way to do it ... ideally it would be done through communicating with the seam/hibernate validation system and just recording the validation results but as far as I can tell there isn't a way to do this?
Has anyone done anything like this in the past? There are a couple nasty work arounds but I'd prefer to do it cleanly.
Just a quick overview of the process that we have happening right now for context:
1) user enters field value
2) onblur value is set with ajax (a4j:support) at this point the validators fire and the div is re-rendered, if any validation errors occured they're now visible on the page
What I'd like to have happen at step2 is a 'ValidationListener' or something similar is called which would allow us to record the results of the validation.
Thanks if anyone is able to help :o
You should be able to do it by creating a Bean that has a method observing the org.jboss.seam.validationFailed event. That method can then do whatever logging you want.
#Name("validationObserver")
public class ValidationObserver() {
#Observer("org.jboss.seam.validationFailed")
public void validationFailed() {
//Do stuff
}
}
The validationFailed event doesn't pass any parameters so you'll have to interrogate the FacesMessages or possibly the Hibernate Validation framework itself if you want to record what the error was.
I you are only using Hibernate for validation, you can use the Hibernate ClassValidator in the validationFailed() method, as recommended by Damo.
Example:
public <T> InvalidValue[] validateWithHibernate(T object) {
ClassValidator<T> validator = new ClassValidator(object.getClass());
InvalidValue[] invalidValues = validator.getInvalidValues(object);
return invalidValues;
}

Categories