Save intermediate input by AJAX ICallbackEventHandler when form session is timeout
For large form with tons of editor controls, if user does not finish the input before it’s timeout, user will have to fill the form again from scratch. That would be frustrating :(
A user-friendly workaround is to prompt user the choice to give up intermediate result or save it, when user tries to save the form and find it is timeout. If user chooses to save it should save anything that have inputted by a partial request to server.
To implement ICallbackEventHandler interface for the editor page is able to control the round-way interaction between server code and client scripts.
The authentication of my project is in form based mode, so have to compromise the security to move the editor page out of the authentication scope. By achieving that, need add following code to web.config.
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
The FormPageName.aspx is derived from PageBase.cs, so the session timeout code will be put in the OnInit method of the base page. We need define an event in the base class, which is used to notify inheritted page when timeout happens.
public delegate void TimeOutDelegate();public TimeOutDelegate OnTimeOut;
protected override void OnInit(EventArgs e)
{
if (Context.Session != null)
{
if (Session.IsNewSession)
{
string strCookieHeader = Request.Headers["Cookie"];
if (strCookieHeader != null && strCookieHeader.ToUpper().IndexOf("ASP.NET_SESSIONID") >= 0)
{
//On timeouts, redirect to timeout page.
if (Page.IsPostBack && OnTimeOut != null)
{
TimeOutArgs arg = new TimeOutArgs();
OnTimeOut();
}
}
}
}
base.OnInit(e);
In actual page, we need implement ICallbackEventHandler, that’s the critical part of this solution. That requires to implement RaiseCallbackEvent method and GetCallbackResult function, which are respectively process the AJAX request from client side and return a string typed result back to client.
public partial class FormPageName : PageBase,ICallbackEventHandlerprivate string _callbackResult = "false";
public string GetCallbackResult()
{
return _callbackResult;
}
public void RaiseCallbackEvent(string eventArgument)
{
string[] args = eventArgument.Split(new string[] { "^^" }, StringSplitOptions.None);
if (args.Length == 3 && args[0].Equals("SaveTempChanges"))
{
try
{
int companyID =0;
if (Int32.TryParse(args[2], out companyID) == true
&& companyID != 0
&& !String.IsNullOrEmpty(args[1]))
SaveData(companyID, args[1].ToString());_callbackResult = “true”;
}
catch
{ }
}
}
protected override void OnInit(EventArgs e)
{
string cbReference = this.ClientScript.GetCallbackEventReference(this,
"arg", "ReceiveServerData", "context", false);
string callbackScript = "function CallServer(arg, context)"
+ "{ " + cbReference + ";}";
this.ClientScript.RegisterClientScriptBlock(this.GetType(),
"CallServer", callbackScript, true);
base.OnTimeOut += new TimeOutDelegate(TimeOutProcessing);
base.OnInit(e);
}
protected void TimeOutProcessing()
{
if(Page.IsPostBack)
ClientScript.RegisterStartupScript(this.Page.GetType(),
"callTimeoutProcessing", "ConfirmSaveTempPageState();", true);
}
The code line “this.ClientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "context", false); ” is to generate a JavaScript code to make partial request to server end function. It indicates the name of JS callback function as “ReceiveServerData”, which should be coded to process the result from the partially requested server function. Parameters “arg” and “context” respectively represents the argument list and context info that should be passed via the request; and finally the generated partial request JS code will be wrapped up in the function called as CallServer. When page is loaded, it will generate scripts like this:
<script language="javascript" type="text/javascript">
function CallServer(arg, context){
WebForm_DoCallback('__Page',arg,ReceiveServerData,context,null,false);
}
</script>
if (arg == "true") {
var context = $("input[id$='hidUserID'")[0].value + "^^" + $("input[id$='hidCompanyID'")[0].value;
CallServer("SaveTempChanges^^" + context, context);
}
}
function ReceiveServerData(rValue, context) {
CloseAndRebind();
}
function ConfirmSaveTempPageState() {
if (confirm('Your session is timeout, do you want to save your current changes?'))
confirmCallBackFn("true");
else
CloseAndRebind();
}
The confirm popup will leave user the choice to persist middle result or give it up. If user chooses not to save, it will close the current page, and refresh the back-end openner page for routing back to login page. However, if user chooses to save, it will make the partial request by calling CallServer function, which will consequently trigger the function WebForm_DoCallback to call server end function, so that the RaiseCallbackEvent routine will get invoked and after completing save the page data, the GetCallbackResult function will return client side a response. The response will be processed by the client function ReceiveServerData and the argument ‘rValue’ contains the returned string of GetCallbackResult function.