Spring portlet mvc Validation fails during submission of the editted form - spring

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";
}

Related

RequestMapping isn't reaching in the controller and thus list isn't showing on the JSP page

I'm developing a simple application of tender management in which I'm using J2EE, Spring-mvc and CRUD operations on Eclipse IDE
I've to retrieve the list of tenders from the database and show in the JSP using controller. The JSP page is mapped in the #RequestMapping but the mapping isn't executing and hence the code following it isn't reaching.
I've tried non- crud operations before and have used different methods including ModelAndView and String but the issue is with mapping which isn't able to run when loading the required JSP. No error is being shown.
Here is the mapping call in the Controller
#RequestMapping("/viewTendersByUser")
public String viewTender(Model m) {
System.out.println("in edit tender user controller");
List<tender> tender = userDao.getTenderByCreator(name);
m.addAttribute("tender", tender);
System.out.println("in ctender after view tender");
System.out.println(""+tender);
return "viewTendersByUser";
}
This is the method in DAO which I'm calling but isn't actually reached
#Override
public List<tender> getTenderByCreator(String name) {
String sql = "select * from tenderdb where publisher =
'"+name+"'";
return jdbcTemplate.query(sql,new RowMapper<tender>(){
public tender mapRow(ResultSet rs, int row) throws SQLException {
tender t = new tender();
t.settName(rs.getString(1));
t.setOpening(rs.getTimestamp(2));
t.setClosing(rs.getTimestamp(3));
t.setMinBid(rs.getDouble(4));
t.settDesc(rs.getString(5));
return t;
}
});
}
And this is the JSP I'm trying to show data in:
<h3>List of Tenders</h3>
<table border="2" width="70%" cellpadding="2">
<tr><th>Tender Name</th><th>Opening Date</th><th>Closing Date</th><th>Minimum Bid</th><th>Tender Description</th><th>Edit</th><th>Delete</th></tr>
<c:forEach var="row" items="${tender}">
<tr>
<td>${row.tName}</td>
<td>${row.opening}</td>
<td>${row.closing}</td>
<td>${row.minBid}</td>
<td>${row.tDesc}</td>
<td>Edit</td>
<td>Delete</td>
</tr>
</c:forEach>
</table>
Expected results should be the list of tenders from the database onto the viewTendersByUser.jsp.
Actual result is the JSP page being loaded but the contents to be received from controller aren't showing which traces to mapping not being called and actually nothing is happening.
Even an error or exception would be easy to debug but there's no output.

Not able to perform AJAX call for MVC contoller

I just want to use ajax call for my Spring MVC controller but Ajax call not hitting to the controller's method. Am I doing it wrong?
My Jsp Code [ModifyUser.jsp]:
<form name="testForm" >
<table>
<tr><th>User ID</th><td><input id="user_id" type="text" value="AD001"
readonly="readonly"> </td></tr>
<tr><th>Name</th><td><input id="user_name" type="text" value="ABC SBC ">
</td></tr>
<tr><th>E-mail</th><td><input id="user_email" type="text"
value="asd#xyz.com"> </td></tr>
</table>
<form>
<script>
function deleteUser()
{
alert("going to delete user");
$.ajax({
type : "GET",
url : "${pageContext.request.contextPath}/deleteUserReq",
data : {
"usrId" : ${user_id}
},
success: function(data){
//response from controller
alert(data);
}
});
}
</script>
My Controller code:
#RequestMapping("deleteUserReq")
#ResponseBody
public ModelAndView inactiveUserReq(#RequestParam HttpServletRequest
request, HttpServletResponse response, Model model) {
System.out.println("===== going to delete user ===== ");
/*String userChk=reportDAOImpl.inactiveUser(userID);*/
String userChk="success";
System.out.println(" === "+userChk);
return new ModelAndView("ModifyUser","responsetxt",userChk);
}
you should not return model and view with response body,either string
or something that can be converted to json by jackson (Map,ArrayList)
#RequestMapping("deleteUserReq")
#ResponseBody
public String inactiveUserReq(#RequestParam HttpServletRequest
request, HttpServletResponse response, Model model) {
System.out.println("===== going to delete user ===== ");
/*String userChk=reportDAOImpl.inactiveUser(userID);*/
String userChk="success";
System.out.println(" === "+userChk);
//return new ModelAndView("ModifyUser","responsetxt",userChk);
//return string or json
return "success";
}
I hope it helps
URl is not calling deleteUserReq controller
http://localhost:8085/CdfPortal/userModify?usrId=BU941003 [Actual result]
http://localhost:8085/CdfPortal/deleteUserReq?usrId=BU941003 [Required]

Spring MultipartFile causing form validation to not be mapped

I'll start by saying my knowledge of Spring is very limited. However, I've been able to work through issues I've faced with it in the past. My newest problem isn't making much sense to me.
So what I've got is a form which takes attributes for an item to be sold at auction. This form has an optional field which can upload a picture of the item being sold. The image uploading works as is. I noticed my form wasn't actually showing the errors given during validation, so I started looking at what might be causing that. If I remove the MultipartFile from the method signature, the web will correctly show form validation errors if they exist. However, now I don't have the image I need.
On the flip side, if I add the required = false attribute to the RequestParam on the MultipartFile, my issue persists and when a form doesn't meet the validations set, I'm met with the following.
The Java side for this method that's supposed to save the item if it's valid or show the validation errors is as follows:
#RequestMapping(method = RequestMethod.POST)
public ModelAndView save(#Valid Item item, #RequestParam(name = "itemImage", required = false) MultipartFile file,
BindingResult result, RedirectAttributes redirect) {
if (result.hasErrors()) {
return new ModelAndView("item/save", "formErrors", result.getAllErrors());
}
boolean isCreate = (null == item.getId());
if (file != null && !file.isEmpty()) {
if (isCreate) {
item = itemService.save(item);
}
Path directory = Paths.get(itemImageDir + "/" + item.getAuction().getId() + "/" + item.getId());
if (!Files.exists(directory)) {
try {
Files.createDirectories(directory);
} catch (IOException e) {
e.printStackTrace();
}
}
try {
Files.copy(file.getInputStream(), Paths.get(directory.toString(), file.getOriginalFilename()),
StandardCopyOption.REPLACE_EXISTING);
item.setImageUrl(String.format("/items/image/%s/%s/%s", item.getAuction().getId(), item.getId(), file
.getOriginalFilename()));
itemService.save(item);
} catch (IOException | RuntimeException e) {
result.addError(new ObjectError("imageUrl", "Failed to upload " + file.getOriginalFilename() + " => "
+ e.getMessage()));
return new ModelAndView("item/save", "formErrors", result.getAllErrors());
}
} else {
itemService.save(item);
}
String message = "Successfully created a new item.";
if (!isCreate)
message = "Item has been successfully updated.";
redirect.addFlashAttribute("globalMessage", message);
return new ModelAndView("redirect:/auctions/{item.auction.id}", "item.auction.id", item.getAuction().getId());
}
The view for this page, without all of the extra fluff, looks as so:
<form id="auctionForm" class="col-xs-12" th:action="#{/items/(item)}" th:object="${item}"
action="#" method="post" enctype="multipart/form-data">
<div th:class="'form-group row'">
<label for="itemImage" class="control-label col-sm-2"> Image Upload: </label>
<div class="col-sm-4">
<input id="itemImage" type="file" name="itemImage"/>
</div>
</div>
</form>
Extra context for the issue: If I remove the #Valid annotation, the method will be called and won't fail when the form isn't valid. However, when I have #Valid, the controller method is not even hit. Is there a way I can check where it's failing if it isn't getting to the controller? I compared this one controller with all of the others and it seems to follow the same pattern.
If anyone has any suggestions, I'd be more than grateful. I don't really have any idea what I'm missing, so any suggestions are welcome.
Add in controllers method sigMultipartHttpServletRequest mrequest and check it once
Alright, so I figured out what I was missing. Being new to Spring, I didn't realize parameter order could matter. Apparently BindingResult has to immediately follow the parameter you want to validate. So, I changed the method signature to the following and now all is working as intended:
public ModelAndView save(#Valid Item item, BindingResult result, #RequestParam(name = "itemImage", required = false) MultipartFile file,
RedirectAttributes redirect) {

How to check if a particular row has validation error for a multi row validation in Spring using BindingResult

The current logic will check if the BindingResult has errors and the display the data and errors in a jsp.
The logic needed is to check Errors for each row and display only those rows containing validation errors and update the rows which don't have validation errors.
#Autowired
private IncidentExtractStgService incidentExtractStgService;
#RequestMapping(value = "/validatingIncidentList", method = RequestMethod.POST)
public String ValidateIncidentList( #Valid #ModelAttribute("incidentsForm") IncidentsForm incidentsForm,
BindingResult bindingResult,RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
for(ObjectError error: bindingResult.getAllErrors()){
System.out.println(error);
}
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.incidentsForm", bindingResult);
redirectAttributes.addFlashAttribute("incidentsForm", incidentsForm);
return "redirect:/validateIncidentList";
}
else
{
for(IncidentExtractStg ie : incidentsForm.getIncidents()) {
ie.setValidated(1);
incidentExtractStgService.update(ie);
System.out.println(ie.getNumber()+" "+ie.getWaitTime());
}
return "redirect:/validateIncidentList";
}
The below code snippet will check if the model contains attribute "incidetsForm",if so will send the same to example.jsp which in-turn will display the data and validation errors.
#RequestMapping(value = "/validateIncidentList", method = RequestMethod.GET)
public String incidentList(Model model) {
if (!model.containsAttribute("incidentsForm")) {
List<IncidentExtractStg> incidents = incidentExtractStgDao.validateList();
incidentsForm.setIncidents(incidents);
model.addAttribute("incidentsForm", incidentsForm);
return "example";
}
model.addAttribute("errormessage","Please Check the Validation Errors column for Errors");
return "example";
}
Example.jsp code snippet
<c:forEach var="ie" items="${incidentsForm.incidents}" varStatus="status">
<tr>
<td><form:input path="incidents[${status.index}].id" value="${ie.id}" readonly ="true"/></td>
<td><form:errors path="incidents[${status.index}].id" cssClass="error" /></td>
<td><form:input path="incidents[${status.index}].number" value="${ie.number}"/></td>
<td><form:errors path="incidents[${status.index}].number" cssClass="error" /></td>
</tr>
IncidentsForm.java:
import java.util.List;
import javax.validation.Valid;
import com.infosys.sla.model.IncidentExtractStg;
public class IncidentsForm {
#Valid
private List<IncidentExtractStg> incidents;
public List<IncidentExtractStg> getIncidents() {
return incidents;
}
public void setIncidents(List<IncidentExtractStg> incidents) {
this.incidents = incidents;
}
}
IncidentExtractStg.java snippet
#Entity
#Table(name="incident_extract_stg")
public class IncidentExtractStg {
#Id
#Column(name="ies_id")
private int id;
#NotBlank(message="number cannot be empty")
#Pattern(regexp="[A-Za-z0-9]*",message="number can contain only alphabets and numbers")
#Column(name="ies_number")
private String number;
First of all, if I were you I will extract all the logic inside a Service Layer. To proceed, you could create an interface IncidentService and its own concrete implementation IncidentServiceImpl in which you can safely will handle your needs. Controllers are definitely not done to do everything.
Then, what are your needs?
"check Errors for each row and display only those rows containing validation errors and update the rows which don't have validation errors"
A method inside your Service Layer could be like that:
public void handleErrors(IncidentsForm incidentsForm, BindingResult bindingResult){
List<String> fieldsInErrorState = new ArrayList<String>(10);
if (bindingResult.hasErrors()) { //
Map<String, Object> bindingModel = bindingResult.getModel();
for (Map.Entry<String, Object> entry : bindingModel.entrySet()) {
String key = entry.getKey();
//Object value = entry.getValue(); you don't need to parse that unless you want specific domain model handlers to run
//you need to store the key as a form field which is in error state
fieldsInErrorState.add(key);
//you already have all the stuff to parse and display errors in your JSP
//thanksfully to bindingResult and JSTL tags.
}
ContactMessageForm cmForm2 = new ContactMessageForm();
// get the list of the fields inside your form
Field[] declaredFields = ContactMessageForm.class.getDeclaredFields();
for (Field field : declaredFields) {
if (!fieldsInErrorState.contains(field.getName())) {
if (field.getName().equalsIgnoreCase("firstname")) {
cmForm2.setFirstname(contactMessageForm.getFirstname());
}
if (field.getName().equalsIgnoreCase("lastname")) {
cmForm2.setLastname(contactMessageForm.getLastname());
}
//etc for each properties of your form object.
}
// then store your dbmodel object
// BUT i think you must be carefull to your data integrity... It is maybe not safe to save an object like that with bypassing some stuff...
// Your form was built like that maybe for a good reason looking at your objects graph.
// If your form is too big, then split it in small parts, it will be much easy to handle, to update, and to work with daily.
}
}
}
Of course you need to customize that code, don't forget to add the throws IntrospectionException to your service method, and you are on the good way.
Cheers!
As the logic is to display only those rows containing validation errors, a new List is created to store the rows which are having at least one validation error.
A new BindingResult is created to store the Errors against the index of the new List.(If this is not done then the error message wont be displayed against the displayed row).
The below logic is to check for field error against each field of the row and from the jsp you can see the filed name has "incidents[${status.index}].id".
- Counter i to get the row count
- Counter j is to set the index for BindingResult.
BeanPropertyBindingResult result2 = new BeanPropertyBindingResult(incidentsForm, bindingResult.getObjectName();
List<IncidentExtractStg> incidents= new ArrayList<IncidentExtractStg>();
int i=0;// to get the row count
int j=0;// to set the index
for(IncidentExtractStg ies : incidentsForm.getIncidents())
{
int count=0;
Field[] declaredFields = IncidentExtractStg.class.getDeclaredFields();
for (Field field : declaredFields)
{
if (bindingResult.hasFieldErrors("incidents["+i+"]."+field.getName()))
{
for (FieldError error: bindingResult.getFieldErrors("incidents["+i+"]."+field.getName()))
{
result2.addError(new FieldError(error.getObjectName(), "incidents["+j+"]."+field.getName(), error.getRejectedValue(), error.isBindingFailure(), error.getCodes(), error.getArguments(), error.getDefaultMessage()));
}
count++;
}
}
if(count>0)
{
j++;
incidents.add(ies);
}
else
{
ies.setValidated(1);
incidentExtractStgService.update(ies);
}
i++;
}
i=0;
j=0;
if (bindingResult.hasErrors()) {
incidentsForm.setIncidents(incidents);
System.out.println("error block");
for (FieldError error: result2.getFieldErrors()) {
System.out.println("field errors are "+error.getField());
System.out.println("field errors are "+error);
}
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.incidentsForm", result2);
redirectAttributes.addFlashAttribute("incidentsForm", incidentsForm);
return "redirect:/validateIncidentList";
}
E.g: If in row number 30 ,incidents[30].number field has validation error. Then i=30 and count>0 and j=0.
Hence the entire row incidents[30] will be saved at index 0 in the newly initialized list and the bindingresult will also be added at index 0. If the J is not being set to the result2 and bindingresult is used then it will still point to index 30 and error message will not be displayed against the field which is now stored at index 0.
Now this updated IncidentsForm and result2 will be send to jsp page which will display only those rows having validations errors and there respective error messages.

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

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";
}

Resources