Form validation in Spring - spring

I want to show errors for all the form, not single field. So I did this:
public void validate(Object target, Errors errors) {
AddUserForm addUserForm = (AddUserForm) target;
if (addUserForm.getPassword().length() == 0) {
errors.reject("Message error");
}
}
Now I'm using reject method instead of rejectValue, because I don't need to set error message to one field.
And this is my jsp:
<form:form method="POST" commandName="addUserForm"
class="form-horizontal" style="width: 510px;">
<form:errors ??? />
...
Which attribute should I use now? I was trying this:
<form:errors commandName="addUserForm"/>
But doesn't help.

If you are rejecting by using a fieldName in form object as:
errors.rejectValue("fieldName", "errorMessage");
This you can display in view as:
<form:errors path="fieldName"/>
If you are rejecting without specifying the fieldname in form object i.e., raising the error on whole form object then you can set these errors as:
errors.reject("Message error");
This you can display in view without any attributes as:
<form:errors/>

Related

Why do I lose information after submit a form with Spring MVC?

As I say int the title I loose information in the object that comes back from JSP to Controller.
From my Controller I pass a ModelAndView with an object of class Historic.
In the JSP page I have access to all of the values of this object, but when I submit I just get part of this information, some looses on the way on.
Controller:
#GetMapping("/tt")
public ModelAndView index(Model model) {
HistoricBO historic = new HistoricBO();
// ... I fulfill this object ...
return new ModelAndView("tt", "historic", historic);
}
In JSP I have access to all the information that I passed.
I use the values in two different ways. The first one (information that later I won't be able to recover) is:
<form:form method="POST" action="/addInput" modelAttribute="historic">
....
<form:label path="userHistoric[0].user.name" />
<form:input path="userHistoric[0].user.name" disabled="true" />
Being userHistoric a list inside HistoricBO object.
And the other way that I use the object values is daoing loop to the registers and show them. I can have these values after submit:
c:forEach items="${historic.userHistoric[0].periods[0].registers}" var="reg" varStatus="rog">
...
<td class="tab-odd">
<form:input path="userHistoric[0].periods[0].registers[${rog.index}].hours[0]" class="monin" type="number" />
</td>
The method that catch the submit is as follows:
#PostMapping("/addInput")
public String savePeriod(
#ModelAttribute("historic") HistoricBO inputs,
BindingResult result, ModelMap model) {
if (result.hasErrors()) {
return "error";
}
...
And here the object inputs only has setted the hours values, the rest of the object is empty.
Can you please why is the info loosing and how to solve it?
Thanks
Remove disabled="true" and use readonly="true" or readonly="readonly" instead like below.
<form:input path="userHistoric[0].user.name" readonly="readonly" />
Disabled values will not be submitted with the form.
See this values-of-disabled-inputs-will-not-be-submitted and demo here.

how to display validation errors from another form submit

Imagine a form with a single field where the user provides their email. On POST, the controller action method sends a confirmation code to that email and displays a second form in which the user is supposed to enter the confirmation code they received. If the code does not match I then return the original view and I would like to display an error message next to the email ("email was not confirmed").
Sample code below:
1st view (asking the email)
<div>
Please provide the following information to sign-up:
<form:form modelAttribute="newAccountInfo" action="signup-submit.do" method="POST">
<div><form:label path="email">Email:</form:label>
<form:input type="text" path="email"/><form:errors path="email" cssClass="error" element="div"/>
</div>
...
1st view controller method
#RequestMapping(path="/signup-submit", method=RequestMethod.POST)
public String signupSubmit(HttpServletRequest request
, #ModelAttribute("newAccountInfo") #Valid NewAccountInfo newAccountInfo
, BindingResult result
, Model model) {
String confirmCode = generateRandomSecret();
// send confirmCode by email to newAccountInfo.email (omitted)
model.addAttribute("emailConfirmation" , new EmailConfirmation());
request.getSession().setAttribute("newAccountInfo", newAccountInfo);
request.getSession(false).setAttribute("email-code", confirmCode);
return View.SIGNUP_EMAIL_CONFIRMATION.name;
}
2nd view (asking the confirmation code)
<div>
Enter the confirmation code that was sent to your email:
<form:form modelAttribute="emailConfirmation" action="signup-email-confirmation-submit.do" method="POST">
<form:label path="code">Confirmation code:</form:label>
<form:input type="text" path="code"/>
<input type="submit" value="Submit" />
</form:form>
2nd view controller method
#RequestMapping(path="/signup-email-confirmation-submit", method=RequestMethod.POST)
public String signupEmailConfirmationSubmit(
#ModelAttribute("emailConfirmation") EmailConfirmation emailConfirmation
, BindingResult result
, Model model) {
if (emailConfirmation.getCode().equals(request.getSession(false).getAttribute("email-code")))
return View.SIGNUP_SUCCESS.name;
else {
model.addAttribute("newAccountInfo", request.getSession(false).getAttribute("newAccountInfo"));
request.getSession(false).invalidate();
// TODO - what should I do here ?
return View.SIGNUP.name;
}
Assuming the confirmation code was not correctly entered, what should I do in the second view controller method so that when the first view is displayed (for the second time), there is a field validation error message next to the email with description "email was not confirmed" ?
In the line marked with the TODO comment I 've tried the following:
result.rejectValue("email", null, "email was not confirmed");
… but that results in the following exception:
org.springframework.beans.NotReadablePropertyException: Invalid property 'email' of bean class [EmailConfirmation]: Bean property 'email' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
(which makes sense as the email is not a field of EmailConfirmation).
However, the below also fails (silently, without an exception, the 1st view is displayed, I just don't see the validation error message):
result.addError(new FieldError("newAccountInfo", "email", "email could not be confirmed"));
In the end, the only way I could get this to work was to add a custom model property by adding the following (in the TODO line of the second view controller method always):
model.addAttribute("emailConfirmationError", true);
… and then modifying the 1st view as follows:
<div><form:label path="email">Email:</form:label>
<form:input type="text" path="email"/>
<form:errors path="email" cssClass="error" element="div"/>
<c:if test="${not empty emailConfirmationError}">
<span class="error">The email could not be confirmed</span>
</c:if>
</div>
The above succeeds but feels like a hack as I am not using the validation machinery of Spring MVC.
My questions are:
what is the idiomatic way to achieve the above
are there any other flawed mental models or misunderstandings present in the above code?

Spring MVC: How do I preserve model attributes in spring validation errors

I searched around on Stack Overflow, but could not find the solution to my query. I have a controller function that adds multiple model attributes on a GET request
#RequestMapping(method = RequestMethod.GET, value = "/showdeletesearchqueryform")
public String showDeleteSearchQuery(final Model model) {
if (LOG.isDebugEnabled()) {
LOG.debug("Fetching all the search query results.");
}
ImmutableList<ArtQueryResults> results = this.searchQueriesService
.getSearchQueries(APPNAME);
// Adding model attribute # 1
model.addAttribute("searchResults", results);
if (LOG.isDebugEnabled()) {
LOG.debug("\"searchResults\" model attribute has been intialized from "
+ results);
}
ArtDeleteQueryRequest request = new ArtDeleteQueryRequest();
request.setAppName(APPNAME);
if (LOG.isDebugEnabled()) {
LOG.debug("Model attribute initialized = " + request);
}
// Adding model attribute # 2
model.addAttribute("deletedAttributes", request);
return "deletesearchqueries";
}
My JSP
<div class="column-group">
<form:form method="POST" action="${pageContext.request.contextPath}/arttestresults/showdeletesearchqueryform" modelAttribute="deletedAttributes">
<form:errors path="*" cssClass="alert alert-danger column lg-units-5 units-2" element="div"/>
<form:hidden path="appName" id="appNameId" htmlEscape="true"/>
<div class = "units-1 column lg-units-12">
<!-- Hidden Key for app name. -->
<form:select path="idsToBeDeleted" id="IdsToBeDeletedSelectId">
<c:forEach items="${searchResults}" var="searchResult" varStatus="loop">
<form:option label="${searchResult.searchQuery}" value="${searchResult.id}" />
</c:forEach>
</form:select>
</div>
<div class="units-1 column lg-units-12">
<%-- This is a hack that make sure that form is submitted on a click. Not sure why form is not being submitted. --%>
<button class="button" type="submit" onclick="javascript:$('form').submit();">Delete Selected Queries</button>
</div>
</form:form>
My controller POST function
#RequestMapping(method = RequestMethod.POST, value = "/showdeletesearchqueryform")
public String deleteSearchQueries(
Model model,
#ModelAttribute(value = "deletedAttributes") #Valid final ArtDeleteQueryRequest request,
final BindingResult result) {
if (result.hasErrors()) {
LOG.warn("There are " + result.getErrorCount() + " validation errors.");
return "deletesearchqueries";
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("The ids to be deleted are " + request.getIdsToBeDeleted());
}
this.searchQueriesService.deleteSearchQueriesById(
ImmutableList.copyOf(request.getIdsToBeDeleted()));
return "redirect:/arttestresults/showdeletesearchqueryform";
}
}
If there is a validation failure, the model attribute searchResults is not being picked up when I return a view on error condition? Is there a way to preserve the other defined model attributes as well?
Seems that you need flash attributes which were added in spring 3.1. Please take a look at example/explanation:
http://viralpatel.net/blogs/spring-mvc-flash-attribute-example/
The get and the post are different requests. What you get in the post request, is only what comes from the form, so only the "deletedAttributes" model attribute and only the fields that are <input> in the JSP.
You need to put again the searchResults model attribute explicitely like you did in get method.
As suggested by M. Deinum, if one or more attribute(s) will be used by all methods in a controller, you can use a #ModelAttribute annotated method to put it (them) in model automatically.
You can also use SessionAttributes model attributes, that is attributes that are stored in session and not in request. But it is hard to have them properly cleaned from session if user do not post the form but go into another part of the application. You have an example of usage ofSessionAttributes` in Spring's Petclinic example.

how to use custom validation message for html form in liferay6 with custom portlet?

I am using liferay to develop a custom portlet (mvc portlet) my problem is that i have one form in jsp page in HTML and and what i want is to validate some field of the forms.and on submit button its redirecting to another page.the validation i have done is working but what happen is when am clicking the submit button its redirect to another page and on that page its just showing default error from liferay. What i want is to display same page if request not completed with the error message near the field which validation has false.
How can i solve this issue?
this is my code to checking validation while add method in action class
public void addRestaurant(ActionRequest request, ActionResponse response) {
log.info("Inside addRegistration");
List<String> errors = new ArrayList<String>();
restaurant rest = RestaurantActionUtil
.getRestaurantFromRequest(request);
boolean restValid = RestaurantValidator
.validateRestaurant(rest, errors);
if (restValid) {
try {
log.info(rest);
restaurant test = restaurantLocalServiceUtil
.addrestaurant(rest);
if (test == null) {
log.error("Restaurant was Found Null");
response.setRenderParameter("jspPage", errorJSP);
return;
}
} catch (SystemException e) {
log.error(e);
/*
* If there is an error then divert the control to the error
* page.
*/
response.setRenderParameter("jspPage", errorJSP);
return;
}
SessionMessages.add(request, "restaurant-added");
return;
} else {
for (String error : errors) {
SessionErrors.add(request, error);
}
SessionErrors.add(request, "error-while-adding");
request.setAttribute("rest", rest);
return;
}
}
This is my validator class
public class RestaurantValidator {
public static boolean validateRestaurant(restaurant rest, List errors) {
boolean valid=true ;
if (Validator.isNull(rest.getName())) {
errors.add("Name-required");
valid = false;
}
if (Validator.isNull(rest.getLicensekey())) {
errors.add("license-key-required");
valid = false;
}
following is my view.jsp code
Restaurant Name*
" />
<span class="help-block">help block</span>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<label>License Key<span class="f_req">*</span></label>
<liferay-ui:error key="license-key-required" message="license-key-required" />
<input type="text" name="licensekey" class="span8" value="<%=restaurantOBJ.getLicensekey() %>"/>
</div>
</div>
the error message is deisplaying on the redirected page with following way rather then on same page i want the error near textbox of name with the error of "Name_required"
The error message is displaying on the redirected page the following way rather then on same page, I want the error near the name-textbox with the error of "Name_required".
what I want is when name is blank then it should not submit the form and give error near text box of name in my view.jsp page.
I would try to answer your question, try to set a actionResponse.setRenderParameter("valid", "NO"); in your addRestaurant method if the validation fails.
And get this parameter in the doView method and set the renderPage accordingly in this method: include(renderPage, renderRequest, renderResponse); at the end of the doView method.
If you can provide all the information in your question with nice formatting like the validator method, <aui:form> and the javascript method called on onSubmit then we can go ahead with looking for other solution.
In the mean-while you can try this out and see if it works. :-)

how to do binding in Spring annotated request parameter?

i have a controller that is using annotation for request mapping and requestParam.
the controller is working fine. However when submitting a command object with array, spring will crap out saying array index out of bound. i am guessing there is something wrong with binding but don't know how to fix it.
to be more specific, in eclipse i would set debugger at the beginning of the controller, and when submitting the form (by hitting a input submit button) eclipse debugger will not trigger and i will see array index out of bound error in console.
the controller is something like this:
#RequestMapping(value = {"/internal/pcsearch.dex", "/external/pcsearch.dex"},
method = {RequestMethod.POST, RequestMethod.GET})
public ModelAndView executeProductCatalogSearch(
HttpServletRequest request,
#RequestParam(value = "cat" ,required = false) String cat,
#RequestParam(value = "brand" ,required = false) String brand,
#ModelAttribute("command") ProductCatalogCommand cmd
){
[edit]
and the jsp is like:
<form name="pForm"
id="pForm"
action="<c:url value="psearch.dex"><c:param name="cat" value="${cat}"/></c:url>"
method="POST"
style="display:inline;">
...
...
<c:forEach var="model" items="${models}" varStatus="modelLinkStatus">
<script>
var modelImg<c:out value="${modelLinkStatus.index}"/>Src = '<c:out value="${model.altModelImage}"/>';
</script>
<spring:bind path="command.models[${modelLinkStatus.index}].modelSkusDisplayed">
<input type="hidden" name="<c:out value="${status.expression}"/>" id="<c:out value="${status.expression}"/>" value="<c:out value="${status.value}"/>"/>
</spring:bind>
<spring:bind path="command.updateCartButton">
<input type="submit" value="<spring:message code="orderEntryMessages.ecatalog.button.addToCart" text="Add to Cart" htmlEscape="yes" />" name="<c:out value="${status.expression}"/>" id="<c:out value="${status.expression}"/>" class="sub_buttons"/>
</spring:bind>
...
and the command object declare the model array as:
private List<ModelLink> models = new ArrayList<ModelLink>();
where modelLink is a custom ds.
the first foreach tag handle the the model command object and the 2nd part is the submit button i clicked on.
i think you should use AutoPopulatingList as models to bind list to view and controller. for example please refer link. This might resolve your problem of index.

Resources