Annotated form validation - conversion failed custom message - spring

I'm analyzing spring-mvc-showcase example project (spring-mvc-showcase github). Confused with the way validation response is presented on JSP page when I hit incorrect date format (Birth Date field on screenshot). How can I make it more user friendly with some custom message, without those ConversionFailedException details?
screenshot:
Annotation-driven validation is applied. Down below is code segment from bean class representing birthDate field.
FormBean.java
#DateTimeFormat(iso=ISO.DATE)
#Past
private Date birthDate;
Method responsible for form submit:
FormController.java
#RequestMapping(method=RequestMethod.POST)
public String processSubmit(#Valid FormBean formBean, BindingResult result,
#ModelAttribute("ajaxRequest") boolean ajaxRequest,
Model model, RedirectAttributes redirectAttrs) {
if (result.hasErrors()) {
return null;
}
// Typically you would save to a db and clear the "form" attribute from the session
// via SessionStatus.setCompleted(). For the demo we leave it in the session.
String message = "Form submitted successfully. Bound " + formBean;
// Success response handling
if (ajaxRequest) {
// prepare model for rendering success message in this request
model.addAttribute("message", message);
return null;
} else {
// store a success message for rendering on the next request after redirect
// redirect back to the form to render the success message along with newly bound values
redirectAttrs.addFlashAttribute("message", message);
return "redirect:/form";
}
}

Note, that you are working with binding errors here. These are thrown long before the actual JSR-303 validation is performed and they override JSR-303 constraint violations for the failed field.
Codes for the binding errors are typeMismatch. So you can add for example this to your messages properties:
typeMismatch.birthDate = Invalid birth date format.
Check JavaDoc for DefaultMessageCodesResolver and DefaultBindingErrorProcessor to discover how Spring's error code resolution works.

Did you used error tag ?
you can use message property in your validation annotation.
like here :
#NotEmpty(message = "serverIP can not be empty")
private String serverIP;

Related

HTTP Status 400 The request sent by the client was syntactically incorrect

This is my controller
#RequestMapping("/offercreated")
public String offerCreated(#Valid Offer offer, Model model, BindingResult result) {
if (result.hasErrors()) {
return "createoffer";
} else {
System.out.println("form validated");
return "offercreated";
}
and my bean is
#Size(min = 5, max = 45)
private String name;
The form is validated when i give the name of between 5 and 45 characters. But when the form is not validated I am getting 400 status error report. I dont know why i am getting the error. Please need help here
BindingResult parameter must follow the parameter being validated immediately. It's described here: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html
org.springframework.validation.Errors / org.springframework.validation.BindingResult validation results for a preceding command or form object (the immediately preceding method argument).
Wow when i change the controller parameters to Model and then Offer its working !!
#RequestMapping("/offercreated")
public String offerCreated(Model model, #Valid Offer offer, BindingResult result) {
if (result.hasErrors()) {
return "createoffer";
} else {
System.out.println("form validated");
return "offercreated";
}
can someone explain why that is ? i am so confused !

SpringBoot/MVC & Thymleaf form validation on POST with URL parameters

I have a form and validation works. The problem comes in when a url parameter was added. The url parameter is a token and is required. So this is what my controller looks like:
#RequestMapping(value = "/resetpassword", method = RequestMethod.GET)
public String showResetForm(ResetPassword resetPassword, Model model,
#RequestParam(value = "token", required = true) String token,
#RequestParam(value = "msg", required = false) String msg){
model.addAttribute("token", token);
return "resetpassword";
}
#RequestMapping(value = "/resetpassword", method = RequestMethod.POST)
public String setPwd(#ModelAttribute("resetPassword") #Valid ResetPassword resetPassword,// RedirectAttributes reDirectAttr,
BindingResult bindingResult, Model model,
#RequestParam(value = "token", required = true) String token,
#RequestParam(value = "msg", required = false) String msg){
if (bindingResult.hasErrors()) {
//reDirectAttr.addFlashAttribute("org.springframework.validation.BindingResult.resetPassword",bindingResult);
//reDirectAttr.addFlashAttribute("resetPassword",resetPassword);
return "resetpassword?token="+token;
}
else {
if (token == null) {
// TODO: no token, what to do here??
return "redirect:/resetpassword?token=\"\"&msg=notoken";
}
ResetPasswordResponseDto response = super.resetUserPassword(
resetPassword.getUname(), resetPassword.getPassword(),
token);
if (response.getPasswordResetResult() == PasswordResetResult.SUCCESSFUL) {
// TODO: it worked, what now?
return "redirect:/login";
} else if (response.getPasswordResetResult() == PasswordResetResult.INVALID_TOKEN) {
// TODO: bad token
return "redirect:/resetpassword?token="+token+"&msg=badtoken";
} else if (response.getPasswordResetResult() == PasswordResetResult.OUT_OF_POLICY_PW) {
// TODO: out of policy pw
return "redirect:/resetpassword?token="+token+"&msg=outofpolicy";
} else if (response.getPasswordResetResult() == PasswordResetResult.LDAP_FAILURE) {
// TODO: other failure
return "redirect:/resetpassword?token="+token+"&msg=error";
}
}
return "redirect:/resetpassword?token="+token+"&msg=error";
//return new RedirectView("resetpassword?token=\"\"&msg=notoken");
}
So I tried a bunch of things but nothing seems to work. Here is what I would like to happen when the view is requested /resetpassword?token=1232453 the view is displayed. Then if the form has errors the url parameter persists in the url and the form displays the errors. Right now I get an error saying that the template cannot be resolved. Ok fair enough, so I tried doing a redirect instead
return "redirect:/resetpassword?token="+token;
and that seems to work, however the URL parameter is lost and the view loses the bindingResult errors. In the code, I posted I also tried FlashAttributes but I just get an error "Validation failed for object='resetPassword'. Error count: 4" which is correct but I need it to show the form and the errors I coded with Thymeleaf. Any help or suggestions would be great!
Resources I have looked at:
Spring - Redirect after POST (even with validation errors)
&
SpringMVC controller: how to stay on page if form validation error occurs
Have you tried returning a ModelAndView instead of just the redirect string? Attributes on the model will be available as URL query parameters.
ModelAndView redirect = new ModelAndView("redirect:/resetpassword");
redirect.addObject("token", token);
redirect.addObject("msg", "error");
return redirect;

ID in Spring-MVC 2.5 edit form using #Controller

I have a problem with the my Controller code. GET works fine (both empty form + form populated from db), POST works fine only for creating new object, but doesn't work for editing. Part of my #Controller class:
#RequestMapping(value = "/vehicle_save.html", method = RequestMethod.GET)
public String setUpForm(#RequestParam(value="id", required = false) Long id, ModelMap model) {
Vehicle v;
if (id == null) {
v = new Vehicle();
} else {
v = vehicleManager.findVehicle(id);
}
model.addAttribute("vehicle", v);
return "vehicle_save";
}
#RequestMapping(value = "/vehicle_save.html", method = RequestMethod.POST)
public String save(#ModelAttribute("vehicle") Vehicle vehicle, BindingResult result, SessionStatus status) {
vehicleValidator.validate(vehicle, result);
if (result.hasErrors()) {
return "vehicle_save";
}
if(vehicle.getId() == null) {
vehicleManager.createVehicle(vehicle);
} else {
vehicleManager.updateVehicle(vehicle);
}
status.setComplete();
return "redirect:vehicle_list.html";
}
The first method creates a vehicle object (including its ID). But the second method gets the same object without the ID field (set to null).
What could I do: manually set vehicle.setID(id from parameters) and then save it to database. This causes JPAOptimisticLockException + I don't like that solution.
Is there a way to pass my Vehicle object with ID to the second method? BTW, I would like to avoid adding hidden ID field to the JSP.
the example you suggested is using session to store the value. the #SessionAttribute is to bind an existing model object to the session. Look at the source code the class is annotated with #SessionAttributes("pet").Which means your model attribute named "pet" is getting stored in session.Also look at the code in processSubmit method of EditPetForm class
#RequestMapping(method = { RequestMethod.PUT, RequestMethod.POST })
public String processSubmit(#ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "pets/form";
}
else {
this.clinic.storePet(pet);
status.setComplete(); //look at its documentation
return "redirect:/owners/" + pet.getOwner().getId();
}
}
I havnt used something like this before.But i guess putting ur id in session is the way
BTW, I would like to avoid adding hidden ID field to the JSP.
This is common solution. What's wrong with it ? You should create hidden input with id.
May be you can try using session, cause you cant store info between two request. But that will be uglier i guess.
Btw, Can you please explain a little why you want to avoid adding hidden fields? I'm little curious

Struts 1 custom validator

I need to write custom validator which will simply check an array of strings for malformed data. This array of course comes from form as a property and actually it values comes from request through html:multibox tags (these are simple ID of elements in a string form). So I want to validate this data.
The problem is that official guide has nothing to say about handling non-string properties. I don't know how to retrieve this array.
Here is the example from struts valiator guide:
public static boolean validateTwoFields(
Object bean,
ValidatorAction va,
Field field,
ActionErrors errors,
HttpServletRequest request,
ServletContext application) {
String value = ValidatorUtils.getValueAsString(
bean,
field.getProperty());
String sProperty2 = field.getVarValue("secondProperty");
String value2 = ValidatorUtils.getValueAsString(
bean,
sProperty2);
if (!GenericValidator.isBlankOrNull(value)) {
try {
if (!value.equals(value2)) {
errors.add(field.getKey(),
Resources.getActionError(
application,
request,
va,
field));
return false;
}
} catch (Exception e) {
errors.add(field.getKey(),
Resources.getActionError(
application,
request,
va,
field));
return false;
}
}
return true;
}
As you can see this perfectly explains how to handle string values, but what about other types ?
I think, you should use PropertyUtils.getProperty() and then make use of the Object returned. You can see the example mentioned in the link below:
http://www.webkaifa.com/jsp/jakartaStrutsCookbook/059600771x/jakartastrutsckbk-chp-8-sect-9.html

What is Model in ModelAndView from Spring MVC?

Having this basic function
protected ModelAndView handleRequestInternal(...) {
...
return new ModelAndView("welcomePage", "WelcomeMessage", message);
}
I know that this will return modelandView.
I know that welcomePage is my viewname so that means something like welcomepage.jsp will get called.
But I am confused with what is Model part.
What is WelcomeMessage and message and how Model work in that scenario?
The model presents a placeholder to hold the information you want to display on the view. It could be a string, which is in your above example, or it could be an object containing bunch of properties.
Example 1
If you have...
return new ModelAndView("welcomePage","WelcomeMessage","Welcome!");
... then in your jsp, to display the message, you will do:-
Hello Stranger! ${WelcomeMessage} // displays Hello Stranger! Welcome!
Example 2
If you have...
MyBean bean = new MyBean();
bean.setName("Mike!");
bean.setMessage("Meow!");
return new ModelAndView("welcomePage","model",bean);
... then in your jsp, you can do:-
Hello ${model.name}! {model.message} // displays Hello Mike! Meow!
new ModelAndView("welcomePage", "WelcomeMessage", message);
is shorthand for
ModelAndView mav = new ModelAndView();
mav.setViewName("welcomePage");
mav.addObject("WelcomeMessage", message);
Looking at the code above, you can see the view name is "welcomePage". Your ViewResolver (usually setup in .../WEB-INF/spring-servlet.xml) will translate this into a View. The last line of the code sets an attribute in your model (addObject("WelcomeMessage", message)). That's where the model comes into play.
It is all explained by the javadoc for the constructor. It is a convenience constructor that populates the model with one attribute / value pair.
So ...
new ModelAndView(view, name, value);
is equivalent to:
Map model = ...
model.put(name, value);
new ModelAndView(view, model);
Here in this case,
we are having 3 parameter's in the Method namely ModelandView.
According to this question, the first parameter is easily understood. It represents the View which will be displayed to the client.
The other two parameters are just like The Pointer and The Holder
Hence you can sum it up like this
ModelAndView(View, Pointer, Holder);
The Pointer just points the information in the The Holder
When the Controller binds the View with this information, then in the said process, you can use The Pointer in the JSP page to access the information stored in The Holder to display that respected information to the client. Here is the visual depiction of the respected process.
return new ModelAndView("welcomePage", "WelcomeMessage", message);
Well, WelcomeMessage is just a variable name for message (actual model with data). Basically, you are binding the model with the welcomePage here. The Model (message) will be available in welcomePage.jsp as WelcomeMessage.
Here is a simpler example:
ModelAndView("hello","myVar", "Hello World!");
In this case, my model is a simple string (In applications this will be a POJO with data fetched for DB or other sources.). I am assigning it to myVar and my view is hello.jsp. Now, myVar is available for me in hello.jsp and I can use it for display.
In the view, you can access the data though:
${myVar}
Similarly, You will be able to access the model through WelcomeMessage variable.
ModelAndView: The name itself explains it is data structure which contains Model and View data.
Map() model=new HashMap();
model.put("key.name", "key.value");
new ModelAndView("view.name", model);
// or as follows
ModelAndView mav = new ModelAndView();
mav.setViewName("view.name");
mav.addObject("key.name", "key.value");
if model contains only single value, we can write as follows:
ModelAndView("view.name","key.name", "key.value");
#RequestMapping(value="/register",method=RequestMethod.POST)
public ModelAndView postRegisterPage(HttpServletRequest request,HttpServletResponse response,
#ModelAttribute("bean")RegisterModel bean)
{
RegisterService service = new RegisterService();
boolean b = service.saveUser(bean);
if(b)
{
return new ModelAndView("registerPage","errorMessage","Registered Successfully!");
}
else
{
return new ModelAndView("registerPage","errorMessage","ERROR!!");
}
}
/* "registerPage" is the .jsp page -> which will viewed.
/* "errorMessage" is the variable that could be displayed in page using -> **${errorMessage}**
/* "Registered Successfully!" or "ERROR!!" is the message will be printed based on **if-else condition**

Resources