Monday, February 25, 2008

Attaching custom client-side data to ASP.NET AJAX WebRequest objects.

Often we need to store some client-side data from request to request. Because the browser supports two simultaneous connections to the server it's easy to do two concurrent requests to the server - and when response is received we want to somehow differentiate between that requests. For example when I click on Button1 - I want to pass let's say exact time when request was invoked, or some javascript object on which I need to perform some actions in endRequestHandler.

So what I want to do - attach some javascript object to the request somehow, to later use it when that request is served by the server and response is sent back. At the same time I don't want to sent that attached object to the server with request and then receive it from the server, because it's not efficient - we can store that object on client side.

To do this I will use userContext Property of WebRequest Class in ASP.NET AJAX library.

I created the sample page. The code looks like:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <style type="text/css">
        .loggerDiv
        {
            width: 200px;
            height: 400px;
            overflow: auto;
            float: left;
            border: solid 1px black;
            padding: 3px;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server" />
    <div>

        <script type="text/javascript">
        var loggerObj = new Logger();
        
        function beginRequestHandler(sender, args) {
            // get WebRequest object
            var request = args.get_request();

            // get button that invoked postBack
            var clickedButton = args.get_postBackElement();
            var customData = 
                {
                    requestStart:new Date(),
                    button:args._postBackElement
                };
            // modify WebRequest, assigning custom data to _userContext property
            request.set_userContext(customData);
        };
        
        function endRequestHandler(sender, args) {
            var response = args.get_response();
            if(response.get_responseAvailable()) {
                // get associated WebRequestObject
                var webRequest = response.get_webRequest();
                // get custom data we assigned to WebRequest earlier  
                var customData =  webRequest.get_userContext();
                if(customData.button && loggerObj) {
                    var loggerDiv;
                    if(customData.button.attributes.getNamedItem("logger")) {
                        loggerDiv = customData.button.attributes.getNamedItem("logger").value;
                        var sb = new  Sys.StringBuilder();
                        
                        sb.append("----------------" + "<br />");
                        sb.append("request start: "+customData.requestStart.toTimeString() + "<br />");
                        sb.append("request end: "+new Date().toTimeString() + "<br />");
                        sb.append("request invoked by: "+customData.button.id + "<br />");
                        // display registered dataItems
                        sb.append("Data items:" + "<br />");
                        var dataItemKey = "";
                        for(dataItemKey in sender._dataItems) {
                            sb.append(dataItemKey + ": " + sender._dataItems[dataItemKey]+"<br />");
                        }
                        sb.append("----------------" + "<br />");
                        
                        loggerObj.log(loggerDiv, sb.toString());
                    }
                }
            }
        };
        
        function Logger(divId) {
            // logs a message into div
            this.log = function(logger, message) {
                if($get(logger)) {
                    $get(logger).innerHTML += message + "<br />";
                    $get(logger).scrollTop = $get(logger).scrollHeight - 400;
                }
            };
            this.clearLog = function(logger) {
                if($get(logger)) {
                    $get(logger).innerHTML = "";
                }
            };

        };
        </script>

        <asp:UpdatePanel runat="server" ID="UP">
            <ContentTemplate>
                <asp:Button ID="Button1" logger="logger1" runat="server" Text="Button1" 
                    onclick="Button1_Click" />
                <asp:Button ID="Button2" logger="logger2" runat="server" Text="Button2" 
                    onclick="Button2_Click" />
                <asp:Button ID="Button3" logger="logger3" runat="server" Text="Button3" 
                    onclick="Button3_Click" />
                <asp:Button ID="Button4" logger="logger4" runat="server" Text="Button4" 
                    onclick="Button4_Click" />
            </ContentTemplate>
        </asp:UpdatePanel>
        <div>
            <div id="logger1" class="loggerDiv">
            </div>
            <div id="logger2" class="loggerDiv">
            </div>
            <div id="logger3" class="loggerDiv">
            </div>
            <div id="logger4" class="loggerDiv">
            </div>
        </div>
    </div>
    </form>

    <script type="text/javascript">
        Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequestHandler);
        Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(beginRequestHandler);
    </script>

</body>
</html>

Code behind:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Threading;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Thread.Sleep(100);
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        ScriptManager1.RegisterDataItem(this, "Button1 - confirmed from the server");
    }
    protected void Button2_Click(object sender, EventArgs e)
    {
        ScriptManager1.RegisterDataItem(this, "Button2 - confirmed from the server");
    }
    protected void Button3_Click(object sender, EventArgs e)
    {
        ScriptManager1.RegisterDataItem(this, "Button3 - confirmed from the server");
    }
    protected void Button4_Click(object sender, EventArgs e)
    {
        ScriptManager1.RegisterDataItem(this, "Button4 - confirmed from the server");
    }
}

 

The most important in this code is how we attach custom object in beginRequestHandler:

var request = args.get_request();

request.set_userContext(customData);

 

And how we retrieve it later:

var response = args.get_response();

var webRequest = response.get_webRequest();

var customData = webRequest.get_userContext();

As you can see I am attaching DataItems to asynchronous responses - this is just demonstration of how can we pass some additional data from server, that can be tracked trough

sender._dataItems

property in endRequestHandler.

 

Not hard to do at all.

You can see online sample.

Also you can download source code for this sample here.

Hope this helps.

Technorati Tags: ,

1 comment:

Ivan N said...

Graciaaaaaaaaas!! So many thankyouuuu! i spent months to resolve the #$&% sys undefined problem.