I have a problem which has only just surfaced itself.
I'm working in a MVC environment. The method names in my interface class match those of the request module and action i.e. ?module=test&action=action
would result in a method called public function test_action() { }
In this particular problem, I have a form which submits to itself. If validation passes, a record is created and I then show the template of another module. This module expects a series of post variables as it is used in two mo开发者_StackOverflowdules.
The problem I'm having is that, if the user successfully passes the validation and attempts to F5 the page, another new record is created etc.
How can I prevent this from happening?
Normally I would header redirect after a successful insert but in this instance I can't.
I would take it a complete other way. I even find redirection an incorrect way of handling this, since changing locations is not meant to overcome logic/form troubles.
The correct solution is:
- Add a unique hash to your form in a hidden input
- Store the hash in a server-side session
- When the form is send, validate the hidden input hash with the hash on your server
- Only execute row insertion when the form validates correctly.
If you are working with Zend Framework, there is a Zend_Form_Element_Hash
class for you.
Developer error:
You need to create a handler page which:
- validating sent data
- insert row
- redirect user
You can / should re-direct to a new page after successful insertion.
As you are working in MVC, you can add a new controller that just calls the view you want to show.
Here is what I do it. This is working for me. Hope it can help anyone else.
//+++ start token +++
//This is to prevent duplicate entry on page reload (F5). 19. If I enter all values, press Record in journal and then press F5, the same values are recorded one more time. Need to prevent
// 3rd. $token_hash_from_input get value of input field name name="' .$_SESSION['token_hash'] .'"
$token_hash_from_input = $_SESSION['token_hash'];
//echo $token_hash_from_input .' token_hash_from_input<br>';
//echo $_POST[$token_hash_from_input] .' $_POST[$token_hash_from_input]<br>';
//var_dump($token_hash, $_POST);
// 4th. $_SESSION['token'] created/set in 1st. Then it as if goes around (at first to input field then after page reload returns here). However $_POST[$token_hash_from_input] is value received directly from input field. User click post and input field value is passed to $_POST[$token_hash_from_input]. Here I compare both.
if ( $_SESSION['token'] != htmlspecialchars($_POST[$token_hash_from_input]) ) {
$token_error .= 'yes';
//echo 'session token and token from input field are not the same <br> ';
}
else {
//echo 'session token is equal to post$token_hash)<br>';
}
// 1st. Create token and pass it to session
$token = sha1(uniqid(mt_rand(), true));
$_SESSION['token'] = $token;
//echo $_SESSION['token'] .' new $_SESSION[token]<br>';//after each page reload new token created. Then this token passed to input form (hidden field). value="' .$_SESSION['token'] .'"
// 2nd. Create token_hash and pass it to session. Token hash is to name input fields name and id. I may not use $token_hash and $_SESSION['token_hash']. Instead of this I can use name="token" and id="token".
$token_hash = sha1(uniqid($time_when_form_submitted .'token' .$_SERVER["REMOTE_ADDR"]));
//echo $token_hash .' $token_hash<br>';
$_SESSION['token_hash'] = $token_hash;
//echo $_SESSION['token_hash'] .' new SESSION$token_hash<br>';
// +++ end token +++
Input field like this
<input type="hidden" name="' .$_SESSION['token_hash'] .'" id="' .$_SESSION['token_hash'] .'" value="' .$_SESSION['token'] .'">
or
<input type="hidden" name="<?php echo $_SESSION['token_hash'] ?>" id="<?php echo $_SESSION['token_hash'] ?>" value="<?php echo $_SESSION['token'] ?>">
I suppose code can be improved (I have no good knowledge php etc)
"Normally I would header redirect after a successful insert but in this instance I can't."
are you facing some error in doing that?
If for whatever reason you can't redirect (Which sounds peculiar) you can use the 'same' mechanism used for data validation to flush the forms after a successful insert.
But that's a really ugly way to go.
One of most common issue which many of the web developers face in their web applications, is that the duplicate records are inserted to the Database on page refresh. If the web page contains some text box and a button to submit the textbox data to the database. In that case when the user insert some data to the textbox and click on the submit button, it will save the record to the Database and then if the user refresh the web page immediately then the same record is again saved to the database as there is no unique keys that can be used to verify the existence of the data, so as to prevent the multiple insertion.
From this behavior we can definitely know that, on the page fresh the button click event is fired. To avoid this problem we can try this method as discuss below.
On page load event save the date/time stamp in a session variable, when the page is first loaded, a Session variable is populated with the current date/time as follows:
*void Page_Load(Object sender, EventArgs e) { if(!IsPostBack) { Session["update"] = Server.UrlEncode(System.DateTime.Now.ToString()); } }*
On the page's PreRender event, a ViewState variable is set to the value of the Session variable as follows:
void Page_PreRender(object obj,EventArgs e)
{
ViewState["update"] = Session["update"];
}
Then these two values are compared to each other immediately before the database INSERT command is run. If they are equal, then the command is permitted to execute and the Session variable is updated with the current date/time, otherwise the command is bypassed as given below:
void btnSubmit_Click(object obj, EventArgs e)
{
string name = "";
string qualification = "";
if (Session["update"].ToString() == ViewState["update"].ToString())
{
if (txtName.Text != "" || txtName.Text != null)
{
name = txtName.Text.ToString();
}
if (txtQualification.Text != "" || txtQualification.Text != null)
{
qualification = txtQualification.Text.ToString();
}
//--- Insert data function should be execute here
string strSql = "INSERT INTO Testdata (Name,Qualification) VALUES ('" + name + "','" + qualification + "')";
SqlConnection ANConnection = new SqlConnection(ConnectionString);
ANConnection.Open();
SqlCommand ANCommand = new SqlCommand(strSql, ANConnection);
ANCommand.ExecuteNonQuery();
ANConnection.Close();
ANConnection.Dispose();
//--End of save data
lblMessage.Text = "Inserted Record Sucessfully
Session["update"] = Server.UrlEncode(System.DateTime.Now.ToString());
}
else
{
lblMessage.Text = "Failure – Due to Page Refresh";
txtName.Text = "";
txtQualification.Text = "";
}
}
精彩评论