CSC/ECE 517 Fall 2009/wiki2 3 bd: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 14: Line 14:
<p> Based on the above, the solution appears complete. But an element is missing: how do we specify/implement the alternate course of action when an invalid token is detected. In fact, given the case where the submit button is reclicked, the second request will cause the loss of the first response containing the expected result. The thread that executes the first request still runs, but has no means of providing its response to the browser. Hence, the user may be left with the impression that the transaction did not complete, while in reality, it may have successfully completed.This tip's proposed strategy builds on the Struts framework to provide a complete solution that prevents duplicate submission and still ensures the display of a response that represents the original request's outcome. The proposed implementation involves the abstract class SynchroAction, which actions can extend to behave in the specified synchronized manner. This class overrides the Action.perform() method and provides an abstract performSynchro() method with the same arguments. Below is the example implementation in Java using struts </p>
<p> Based on the above, the solution appears complete. But an element is missing: how do we specify/implement the alternate course of action when an invalid token is detected. In fact, given the case where the submit button is reclicked, the second request will cause the loss of the first response containing the expected result. The thread that executes the first request still runs, but has no means of providing its response to the browser. Hence, the user may be left with the impression that the transaction did not complete, while in reality, it may have successfully completed.This tip's proposed strategy builds on the Struts framework to provide a complete solution that prevents duplicate submission and still ensures the display of a response that represents the original request's outcome. The proposed implementation involves the abstract class SynchroAction, which actions can extend to behave in the specified synchronized manner. This class overrides the Action.perform() method and provides an abstract performSynchro() method with the same arguments. Below is the example implementation in Java using struts </p>
Eg:
Eg:


public final ActionForward perform(ActionMapping mapping,
public final ActionForward perform(ActionMapping mapping,
Line 23: Line 22:
  ActionForward forward = null;
  ActionForward forward = null;
  if (isTokenValid(request))  
  if (isTokenValid(request))  
{ // Reset token and session attributes reset(request);
{  
  try { // Perform the action and store the results forward = performSynchro(mapping, form, request, response); session.setAttribute(FORM_KEY, form);
  try {  
session.setAttribute(FORM_KEY, form);
  session.setAttribute(FORWARD_KEY, forward);
  session.setAttribute(FORWARD_KEY, forward);
  ActionErrors errors = (ActionErrors) request.getAttribute(Action.ERROR_KEY);
  ActionErrors errors = (ActionErrors) request.getAttribute(Action.ERROR_KEY);
Line 34: Line 34:
  session.setAttribute(COMPLETE_KEY, "true"); }  
  session.setAttribute(COMPLETE_KEY, "true"); }  
catch (IOException e)
catch (IOException e)
  { // Store and rethrow the exception session.setAttribute(EXCEPTION_KEY, e); session.setAttribute(COMPLETE_KEY, "true");
  {  
  throw e;  
  throw e;  
  }  
  }  
catch (ServletException e)  
catch (ServletException e)  
{ // Store and rethrow the exception session.setAttribute(EXCEPTION_KEY, e);
{  
  session.setAttribute(COMPLETE_KEY, "true");  
  session.setAttribute(COMPLETE_KEY, "true");  
  throw e; } }  
  throw e; } }  


else { // If the action is complete if ("true".equals(session.getAttribute(COMPLETE_KEY)))
else {  
  { // Obtain the exception from the session Exception e = (Exception) session.getAttribute(EXCEPTION_KEY); // If it is not null, throw it if (e != null) { if (e instanceof IOException) { throw (IOException) e; }  
  {  
 
throw it if (e != null) { if (e instanceof IOException) { throw (IOException) e; }  
else if (e instanceof ServletException)  
else if (e instanceof ServletException)  
{ throw (ServletException) e;  
{ throw (ServletException) e;  
} } // Obtain the form from the session ActionForm f = (ActionForm) session.getAttribute(FORM_KEY); // Set it in the appropriate context if ("request".equals(mapping.getScope()))  
} } context if ("request".equals(mapping.getScope()))  
{ request.setAttribute(mapping.getAttribute(), f); }  
{ request.setAttribute(mapping.getAttribute(), f); }  


else { session.setAttribute(mapping.getAttribute(), f); } // Obtain and save the errors from the session saveErrors(request, (ActionErrors) session.getAttribute(ERRORS_KEY)); // Obtain the forward from the session forward = (ActionForward) session.getAttribute(FORWARD_KEY); }  
else { session.setAttribute(mapping.getAttribute(), f); }  
else { // Perform the appropriate action in case of token error forward = performInvalidToken(mapping, form, request, response); } }
(ActionErrors) session.getAttribute(ERRORS_KEY));  
session.getAttribute(FORWARD_KEY); }  
else {  
  return forward; }
  return forward; }



Revision as of 00:02, 10 October 2009

Synchronizer token pattern

Introduction

Web application developers and designers face the problem with maintaining normal flow of control when multiple form submissions are made in a website . This situation typically occurs when a user clicks more than once on a submit button before the response is sent back or when a client accesses a view by returning to a previously bookmarked page. Control flow sequence is particularly important to preserve when form submission involves transaction processing on the server. There are many solutions to the situation ,some websites just warn the user not to submit twice once a submission is made and to wait for the response .In other cases we have client side and server side solutions to the problem .Server side solution being more reliable than client side .In the client side model the button is disabled based on a flag which is set once first submission is performed .Below we discuss some the ways this is handled in different in different languages -

Java

Java uses the server side solution to solve this problem .The basic idea is to set a token in a session variable before returning a transactional page to the client. This page carries the token inside a hidden field. Upon submission, request processing first tests for the presence of a valid token in the request parameter by comparing it with the one registered in the session. If the token is valid, processing can continue normally, otherwise an alternate course of action is taken. After testing, the token resets to null to prevent subsequent submissions until a new token is saved in the session, which must be done at the appropriate time based on the desired application flow of control. In other words, the one-time privilege to submit data is given to one specific instance of a view.

Based on the above, the solution appears complete. But an element is missing: how do we specify/implement the alternate course of action when an invalid token is detected. In fact, given the case where the submit button is reclicked, the second request will cause the loss of the first response containing the expected result. The thread that executes the first request still runs, but has no means of providing its response to the browser. Hence, the user may be left with the impression that the transaction did not complete, while in reality, it may have successfully completed.This tip's proposed strategy builds on the Struts framework to provide a complete solution that prevents duplicate submission and still ensures the display of a response that represents the original request's outcome. The proposed implementation involves the abstract class SynchroAction, which actions can extend to behave in the specified synchronized manner. This class overrides the Action.perform() method and provides an abstract performSynchro() method with the same arguments. Below is the example implementation in Java using struts

Eg:

public final ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { HttpSession session = request.getSession();

ActionForward forward = null;
if (isTokenValid(request)) 

{

try { 

session.setAttribute(FORM_KEY, form);

session.setAttribute(FORWARD_KEY, forward);
ActionErrors errors = (ActionErrors) request.getAttribute(Action.ERROR_KEY);
if (errors != null && !errors.empty()) 

{

saveToken(request); 
} 
session.setAttribute(ERRORS_KEY, errors); 
session.setAttribute(COMPLETE_KEY, "true"); } 

catch (IOException e)

{ 
throw e; 
} 

catch (ServletException e) {

session.setAttribute(COMPLETE_KEY, "true"); 
throw e; } } 

else {

{ 

throw it if (e != null) { if (e instanceof IOException) { throw (IOException) e; } else if (e instanceof ServletException) { throw (ServletException) e; } } context if ("request".equals(mapping.getScope())) { request.setAttribute(mapping.getAttribute(), f); }

else { session.setAttribute(mapping.getAttribute(), f); } (ActionErrors) session.getAttribute(ERRORS_KEY)); session.getAttribute(FORWARD_KEY); } else {

return forward; }


As we can see from the above code that for a valid token the protected action is performed only once ,while the action is being performed any other requests are received then they are directed to performInvalidToken() method until the action is completed ,this method simply returns an ActionForward named "synchroerror" ,this should lead to a form with a button for continue , this just resubmits without any parameters and nothing happens until action of first form submission is completed .When the action completes, it stores its forward, form, exception, and errors, if any, in the session, and it sets a flag to indicate it has completed. The first request coming after the action completion will get the forward, form, exception, and errors from the session and continue as if it was the first request itself.

PHP

The following method is one of the ways of creating a Synchronous token pattern

The below given code needs to be added to ur code

<?php session_start(); $secret=md5(uniqueid(rand(), true)); $_SESSION['FORM_SECRET']=$secret; ?>

Here uniqueid() is used to create Unique ID. Then md5() is used to create 32 character hash of this Unique ID. This unique id is stored in the session for later use. We also need to use a different session variable for each form.

Then we add a hidden field anywhere in the form using the below given code. <input type="hidden" name="form_secret" id="form_secret" value="<?php echo $_SESSION['FORM_SECRET'];?>" />

we need to compare the value of the hidden field with the value stored in the session. If the value is the same, that means its the first time. Hence process the form data. Now unset the values stored in this session. now when the user refreshes the page, the code for processing form will be skipped. It can be done as given below

<?php session_start(); //get the hidden value $form_secret=$_POST['form_secret']; if(isset($_SESSION['FORM_SECRET'])) { if(strcasecmp($form_secret,$_SESSION['FORM_SECRET'])===0) { /*We need to have the form submission code here

  • /

unset($_SESSION['FORM_SECRET']); }else { //this is the condition of key which is invalid } }else { //this is the case where is secret key is no there echo 'Form data has been processed once!'; } ?>


JavaScript

     Java Script uses two methods to prevent Duplicate form submissions .The first method disables the button and the second method uses cookies to prevent multiple submissions.

Eg:

a. <SCRIPT LANGUAGE="JavaScript" TYPE="text/JavaScript">

function formControl(submitted)
{ if(submitted=="1") 
{ commentForm.Submit.disabled=true alert

("Thanks for your comments!") } } </SCRIPT>


The JavaScript event that fires this script is the onClick event. You're telling it to disable the SUBMIT button after the button is clicked. At that point, the button value equals "1." We included a JavaScript ALERT box in this example so that you'll know the form is working. To tie this JavaScript to our form, we change the form's SUBMIT button to call the JavaScript function. This is done by adding an onClick event to the appropriate INPUT tag, as shown below.

<FORM action="" method="post" enctype="text/plain" name="commentForm">

How did you find our site? <input type="text" maxlength="30" size="20">

<input type="submit" name="Submit" value="Send Comments and Get a Cookie" onClick="formControl(1)"> </FORM>

The problem with this approach is only Internet explorer supports the disable option ,so it is browser dependent.

b.

The Below code has to be inserted into head section of our code

<script> </script>

When a user submits the form, the function sets an identifying cookie. Before the form processes, the function checks to see if there is already a cookie set. If not, the form handles the submission as normal. If there is a cookie, the user gets an alert box that says "You've already submitted your answers" and the form submission is blocked. This is a good, cross-browser way to avoid duplicate submissions, but we have to be careful that user's browser must accept cookies and have JavaScript turned on for this method to work.


ASP.NET

<%@ Page Language="VB" %> <script runat="server"> Dim counter As Integer Sub Page_Load() If Session("counter") Is Nothing Then counter = 0 Else counter = CInt( Session("counter") ) End If If IsPostBack Then counter += 1 Session("counter") = counter End If Label1.Text = "Counter = " & counter.ToString() SafeButton.Attributes.Add("onclick", "disableButton();return true") End Sub

Sub Process_Click(sender As Object, e As EventArgs) Dim i As Long ' 2-second delay Dim endTime As DateTime = DateTime.Now.AddSeconds(2) While DateTime.Now < endTime End While Label2.Text = "Finished @ " & DateTime.Now.ToString() End Sub

Sub Button2_Click(sender As Object, e As EventArgs) Session("counter") = 0 Label1.Text = "Counter = " & Session("counter").ToString() End Sub

</script> <html> <head> <script language="javascript"> function disableButton() { document.form1.SafeButton.style.visibility = "hidden"; } </script> </head> <body> <form runat="server" id="form1">

<asp:Label id="Label1" runat="server">Label</asp:Label>

<asp:Label id="Label2" runat="server">Label</asp:Label>

<asp:Button id="UnsafeButton" onclick="Process_Click" runat="server" Text="Unsafe Button"></asp:Button>

<asp:Button id="SafeButton" onclick="Process_Click" runat="server" Text="Safe Button"></asp:Button>

 <asp:Button id="Button2" onclick="Button2_Click" runat="server" Text="Clear Counter"></asp:Button>

</form> </body> </html>


Here there are two kind of buttons, the Unsafe button allows users to click on submit multiple times. But clicking on the button clicks don't do anything, but I've put a two-second delay into the button handler to emulate the time that a back-end process might take.

The Safe button runs the same process. However, as soon as you click it, it disappears, preventing you from being able to click it again until the processing (postback) is complete.

Ruby On Rails

Ruby on Rails uses the Submit button disabling method to prevent Multiple form submissions .It uses the submit_tag option .Below is the code that demonstrate this

submit_tag "Submit", :disable_with => "Saving..."

This adds behavior to the submit button to disable it once clicked, and to display "Saving..." instead of "Submit".