why does struts reset my form after failed validation? - validation

I am using the validation framework with Struts 1.1.When validation fails, the entire form is reset.
After much time scouring the net, I have gathered:
When a new request is received, the form object is created if it does not exist in the current scope (request or session).
Reset is called()
Form values are populated from the bean properties.
Validation starts if enabled
If validation fails, ActionErrors are returned and the request is directed to the URI given by the input attribute of the action tag in my struts-config.xml.
That's where I have the problem. If validation fails, and I set the input param to the same page, reset() gets called again but it does not use the bean values from when the form is initially loaded. So the user has to re-enter everything.
My action mapping class for this action looks like this:
<action
path="/edit/componentRelease"
type="org.twdata.struts.SpringAction"
name="edit/componentRelease"
scope="request"
input="/WEB-INF/jsp/edit/editComponentRelease.jsp"
parameter="edit/componentRelease"
validate="true"
>
<forward
name="edit/componentRelease"
path="/WEB-INF/jsp/edit/editComponentRelease.jsp"
redirect="false"
/>
</action>
The form used to display the bean starts with:
<html:form method="post" name="componentReleaseEditor" type="com.mx.releasemgr.forms.ComponentReleaseEditorForm" action="/edit/componentRelease">

the reset() is used to clear the values previously entered...if u debug it and see then u'll come to know. eg ur entering 1 in the form and say submit and again come on the same form and enter 2 and submit again now what reset will do is it will clear 1 and now 2, and thus u get 2 in ur validate() part.

#Override
public void reset(ActionMapping mapping, HttpServletRequest request) {
//If Clear button click will set date as today and clear all other value
//If Insert, update with validation as failure than not clear all value on the form, but only clear that wrong when validation (means skipp all value as true on the form and only clear value wrong)
String actionName = request.getParameter("method");
if(actionName!=null){
if (actionName.equals(Enums.ActionName.CLEAR.getActionName())) {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
this.setPublicationDay(dateFormat.format(date));
}
else{
request.setAttribute("book", this);
}
}
super.reset(mapping, request);
}

The solution is to move the display of the form to a different action than that used to forward. In my example, they are both the same.

Related

Changing back the url to original if exception thrown and return back to form

I have a thymeleaf signup form, which if we submit then a controller at "/signup_do" is called which validates and saves the user to database:
<form action="/signup_do" method="post">
...
</form>
The controller at "/signup_do" passes the request to the accountRegistration service method, which does the validation:
#PostMapping("/signup_do")
public String register(Account account, HttpSession session) {
session.setAttribute("accountToRegister", account);
accountManagement.accountRegistration(account);
return "Success";
}
The account registration method can throw an exception SignupFormException, which is handled by the #ExceptionHandler defined in that controller class:
#ExceptionHandler(value=SignupFormException.class)
public String handle(HttpSession session, Model response) {
Account returnDataToForm = (Account) session.getAttribute("accountToRegister");
response.addAttribute("name", returnDataToForm.getFirstName());
session.invalidate();
return "signup";
}
Now the problem is that when exception occurs, the inputs entered in the form is passed back to the signup form, and the entered data remains intact, but the url still remains as /signup_do.
I have tried using return "redirect:/signup" instead, which does change the url, but it ends up making a get request to the /signup url like
/signup?name=John...
but my /signup controller is not designed to handle a get request, it just knows to display the form, so the information is lost.
#GetMapping("/signup")
public String signupPage() {return "signup";}
I also tried using forward:/signup, but that just ended up throwing 405 error.
I figured out a clean workaround a few hours after asking this question.
What I did is change the name of the controller that handles the signup process to ("/signup") as well. Since the controller that displays the page is a #GetMapping("/signup") and the one that handles the signup process is a #PostMapping("/signup") there is no clash.
Now even if the controller changes, the url remains the same, since both of them are signup...
#GetMapping("/signup")
public String signupPage() {return "signup";}
#PostMapping("/signup")
public String register(Account account, HttpSession session) {
session.setAttribute("accountToRegister", account);
accountManagement.accountRegistration(account);
return "success";
}
And this works just like I wanted!!
Redirecting will make a get request to the controller looking for the view to display, which in your situation means losing your data for the reasons you give. I can think of two workarounds:
Don't do the redirect and change the URL manually with javascript everytime you enter this view. If you dislike having a "wrong" URL in a view, editing it manually looks the most reasonable and direct approach. You can see how to do this here, including it in a script that executes everytime the page loads/the submit button is pressed.
Do the redirect and avoid losing your info by storing it in the session for a while longer, accessing it in thymeleaf in this way, instead of getting it from a model attribute. This would mean you would have to be careful to remove this session attributes later. It's also not very "clean" that your get request for the form view includes the user info, so I wouldn't go with this solution if avoidable.

Pass #ModelAttribute to href Spring MVC

I'm attempting to pass a calculated URL from server-side via #ModelAttribute and sometimes the href attribute in html (where I allocate this #ModelAttribute) appears to be empty and need to do multiple ctrl-f5 for finally set the URL.
My controller has something like that:
#Controller
....
String calculatedURL = calculateURL(request, user);
if(StringUtils.isBlank(calculatedURL)){
throw new Exception("Error calculating URL");
}
model.addAttribute("calculatedURL",calculatedURL);
return "myPage";
And in my JSP:
myLink
The problem is that sometimes this href value is "".
I tried to evaluate if calculatedURL is null or blank in my controller and throw exception if is it. This never happens and calculatedURL allways is passed with value.
Thanks in advance and so sorry for my English.
David.

How to disable validation before calling UpdateModel in MVC3 Controller

I'm looking to enable "saving" of form data prior to submission.
I want users to be able to save form progress, even when the form is in an invalid state, and then come back to it at a later time.
Now, my only problem is that I want to be able to use UpdateModel method to update my model. However, as the form is potentially invalid or just partially complete, this will throw an error.
Is there a way to annotate the following method such that validation is ignored in the SAVE instance?
[HttpPost]
public ActionResult Save(Model values)
{
var model = new Model();
UpdateModel(model);
}
I want to save having to write a 1-1 mapping for the elements being saved - which is my fallback option, but isn't very maintainable.
Give TryUpdateModel(model) a try, should fit your needs.
This won't throw an exception, and it will update the model and then return false if there are validation errors.
If you care about the errors, you check the ModelState in the false instance.
Hence, you can use it as so to always save changes:
[HttpPost]
public ActionResult Save(Model values)
{
var model = new Model();
TryUpdateModel(model);
model.saveChanges();
}

How to prevent duplicate form submission in ASP.NET MVC 3?

I have a razor view that renders a html form and it posts to the server.
If the form values are right then it gets saved to database.
After insertion, I redirect to another view where user can make further changes.
Right now the user can hit browser back button and resubmit the form to create another record in db.
How do I prevent duplicate submission in my MVC app?
One solution is to put a hidden "token" field on the form that's generated randomly when the form loads. When you see that token come back on creation store it somewhere temporarily (in session if you're using sessions for example). If you see the same one again, you can assume the same form was submitted twice quickly together.
Create a cookie to represent that particular page when it succeeds. If it is replayed with the cookie (which the browser would now send over with every request) you know not to allow the new attempt.
Redirect the user to another HttpGet action after handling the post request.
So that when the user refreshes the browser the post action will not be called again.
return RedirectToAction("YourActionMethod");
Although client side validation is possible, it is not secure enough.
I am not sure if this method applies to MVC 3, but what i did is implement a ActionFilterAttribute
here is the implementation:
public class PreventFrequentCallsAttribute : ActionFilterAttribute
{
public int DelayRequest = 5;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
var cache = filterContext.HttpContext.Cache;
var originationInfo = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress;
originationInfo += request.UserAgent;
var targetInfo = request.RawUrl + request.QueryString;
var hashValue = string.Join("", MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(originationInfo + targetInfo)).Select(s => s.ToString("x2")));
if (cache[hashValue] != null)
{
filterContext.Controller.ViewData.ModelState.AddModelError("ExcessiveRequests", "Excessive Request Attempts Detected.");
}
else
{
cache.Add(hashValue, originationInfo, null, DateTime.Now.AddSeconds(DelayRequest), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
}
base.OnActionExecuting(filterContext);
}
}
later, in the target controller, just add this attribute:
[PreventFrequentCalls(3)]
public PartialViewResult LogOn(LogOnViewModel model)

Seam: Validation of s:fileUpload?

I've got a JSF page that allows users to upload images. I'd like to perform some validation on the uploaded file to ensure it's the correct size, dimensions, content type, etc. I've created a JSF Validator, and have tried specifying it both in the <s:fileUpload validator="XXX" /> attribute, as well as using the <f:validator /> tag. However, no matter what I try my validator is never called. Is there any way to validate a file upload in Seam? Would using <rich:fileUpload /> allow me to do validation on the uploaded file?
After a lot of trial and error, I've just decided to do a manual validation in my submit method. I haven't found a way to get a Validator object to work successfully.
You can try with a listener like (not sure if event has a cancel method to call if your constraint fails, but you can check):
public void listener(UploadEvent event) throws IOException {
UploadItem item = event.getUploadItem();
String name = "unnamed_attachment";
byte[] data = item.getData();
if (item.isFile()) {
name = FilenameUtils.getName(item.getFileName());
data = FileUtils.readFileToByteArray( item.getFile() );
}
debug("file uploaded '#0' : '#1'", name, item.getFileName());
And some constraints you can put in components.xml, like:
<web:multipart-filter create-temp-files="false"
max-request-size="5200000"
url-pattern="/*"/>

Resources