Spring MVC: How to test whether param exists when there's no value? - spring

I want to display an error message with my custom login.jsp form. When there's an error, the url is ../loginForm?error without any value assigned to error. (This seems to be the behavior of Spring Security.) If there's no error, the url is simply ../loginForm (without the parameter). In the controller I can capture the parameter with #RequestParam, but how do I check whether or not error is passed? In other words, how can I test a parameter alone without a value?
Here's the controller code I have now:
#RequestMapping("/loginForm")
public String showLoginForm(#RequestParam(value="error", defaultValue="false")
boolean error,
Model model)
{
if (error == true)
{
model.addAttribute("loginError", "Invalid username and password.");
}
return "/user/loginForm";
}
...and here's the JSP snippet:
<c:if test="${not empty loginError}">
<tr>
<td><c:out value="${loginError}" /></td>
</tr>
</c:if>
At this point I'm not including the Security configuration I have set up, since everything else seems to be working and I want to keep this focused on the issue at hand.
Thanks in advance for any suggestions!

Ok, I figured it out (while taking a break). The #RequestParam only works when there's actually a parameter available for mapping. If no such parameter is passed in, it's useless. So instead, I checked the Map provided by ServletRequest:
#RequestMapping("/loginForm")
public String showLoginForm(ServletRequest request, Model model)
{
Map<String, String[]> paramMap = request.getParameterMap();
if (paramMap.containsKey("error"))
{
model.addAttribute("loginError", "Invalid username and password.");
}
return "/user/loginForm";
}
It works fine now.

There is another way to do that. Just create one more method where #RequestMapping will check presence of "error" parameter, add required attribute and return view. Both methods could exist together.
#RequestMapping(value = "/loginForm", params = {"error"})
public String loginError(Model model)
{
model.addAttribute("loginError", "Invalid username and password.");
return "/user/loginForm";
}
#RequestMapping(value = "/loginForm")
public String login()
{
return "/user/loginForm";
}

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

Spring portlet mvc Validation fails during submission of the editted form

I have a form with few validations on it.
During new form submission, if validation fails I can see those error messages.
but, during editing the form when I change the field to blank intentionally and submit the form error messages are not shown on Jsp page but I can get the errorcount in controller as 1 .
<portlet:actionURL var="actionUrl">
<portlet:param name="action" value="editCommunity"/>
<portlet:param name="communityID" value="${community.id}"/>
</portlet:actionURL>
<liferay-ui:tabs names="Details" />
<form:form commandName="community" method="post" action="${actionUrl}">
<form:hidden path="id"/>
<div><form:errors cssClass="portlet-msg-error" path="*"/></div>
<table class="manager-detail">
<tr>
<th class="portlet-form-field-label">
<label for="community_label_name"><spring:message code="community.label.name"/></label>
<span class="manager-field-required">*</span>
</th>
<td><form:input id="community_label_name" cssClass="portlet-form-input-field" path="name" size="30" maxlength="80" /></td>
</tr>
My edit controller method.....
rendering edit form
#RequestMapping(params = "action=editCommunity")
public String showEditCommunityForm(final RenderRequest request,
#RequestParam(value="communityID") Long id, final Model model)
throws CommunityNotFoundException {
final ThemeDisplay themeDisplay = (ThemeDisplay) request
.getAttribute(WebKeys.THEME_DISPLAY);
model.addAttribute("community", communityService.getCommunity(id));
return "communityEdit";
}
edited form is submitted
#RequestMapping(params = "action=editCommunity")
public void submitEditCommunityForm(final ActionRequest request,
final ActionResponse response,
#ModelAttribute("community") Community community,
BindingResult result, Model model) throws SystemException, PortalException {
communityValidator.validate(community, result);
if (result.hasErrors()) {
System.out.println("validation errors size..."+result.getErrorCount());
//model.addAttribute("community", community);
response.setRenderParameter("action", "editCommunity");
response.setRenderParameter("communityID", String.valueOf(community
.getId()));
}
}
It is not full code but a block
I have tried couple of things like,
changing the http method from post to POST, but nothing works. Validation perfectly works during form creation, but not during edit.
Am I missing anything? please give me suggestions.
Cheers
Vamshi
Preserving the validation error messages can be a real pain!
I have tried a lot of things - from configuring the redirect behavior of the portlet container to using jsr303 instead of spring validation.
The only solution I have consistently had and success implementing is really ugly:
Do the validation in an action method.
If errors are encountered save the BindingResult/Errors-object with "your own key" in the Spring model and interrupt the action handling.
You are redirected to the render method
There you pick up the Errors-object and put it back to the key where "Spring validation" expects it.
In code this looks something like this:
#ActionMapping
public void invite(#ModelAttribute MyFormBean myFormBean,
BindingResult result, Model model) {
// validate indata
myValidator.validate(myFormBean, result);
// Workaround to preserve Spring validation errors
if (result.hasErrors()) {
model.addAttribute("errors", result);
return;
}
...
}
#RequestMapping
public String showForm(#ModelAttribute("myFormBean") MyFormBean myFormBean,
Model model) {
...
// Workaround to get the errors form-validation from actionrequest
Errors errors = (Errors) model.asMap().get("errors");
if (errors != null) {
model.addAttribute(
"org.springframework.validation.BindingResult.myFormBean", errors);
}
return "myForm";
}
The information stored in the Model under "org.springframework.validation.BindingResult.*" are deleted automatically between the action processing and the render processing, and by preserving it explicitly in "errors" the information will be available to the view.
This is an ugly solution, you have to know more than you want about how the implementation really works, it is counter intuitive and if not properly commented this code could easily be removed by someone not familiar with the problem, but it is not a lot of code and it works.
You can omit the #ModelAttribute in the render phase and retrieve it from the model:
#ActionMapping
public void invite(#ModelAttribute MyFormBean myFormBean,
BindingResult result, Model model) {
// validate indata
myValidator.validate(myFormBean, result);
...
}
#RequestMapping
public String showForm(Model model) {
MyFormBean myFormBean = (MyFormBean)model.asMap().get("myFormBean");
...
return "myForm";
}

Resources