Geeks With Blogs
Amit's Blog Sharing Thoughts and Learning

While visiting Asp.net forum, I found an interesting request, A forum member is trying to implement some validator controls where the member wants to call a Web Service method, since the nature of Asp.net Ajax is asynchronous the call does not return instantly. In the reply, another member from Microsoft provides some code which uses XMLHttpRequest to make a Synchronous call to web service. Certainly it does the Job but there is no flavor of Asp.net Ajax Framework. I am not sure the MS guy really knows that the Asp.net Ajax Framework supports Custom Web Request executor, by default Asp.net Ajax uses Sys.Net.XMLHttpExecutor for server side method calling. So I decide to create a small class which can be used with the Asp.net Ajax Framework to call a Server Side method synchronously. The Following shows the code how to call a server side method synchronously:

function GetWebRequest()
{
    var resultElement = $get('ResultId');

    resultElement.innerHTML = '';

    // Instantiate a WebRequest.
    var wRequest = new Sys.Net.WebRequest();
    // Set the request URL.
    wRequest.set_url('getTarget.aspx');
    // Set the request verb.
    wRequest.set_httpVerb('GET');

    var executor = new Sys.Net.XMLHttpSyncExecutor(); // This is the Custom Executor
    wRequest.set_executor(executor);
    // Execute the request.
    wRequest.invoke();

    if (executor.get_responseAvailable()) 
    {
        // Clear the previous results. 
        resultElement.innerHTML = '';
        // Display Web request status. 
        resultElement.innerHTML += 'Status: [' + executor.get_statusCode() + ' ' + executor.get_statusText() + ']' + '<br/>';
        // Display Web request headers.
        resultElement.innerHTML +=  'Headers: ';
        resultElement.innerHTML += executor.getAllResponseHeaders() + '<br/>';
        // Display Web request body.
        resultElement.innerHTML +=  'Body:';

        if(document.all)
        {
            resultElement.innerText += executor.get_responseData();
        }
        else
        {
            resultElement.textContent += executor.get_responseData();
        }
    }
}

As you can see here is no onComplete handler since this is a synchronous call, I have just ported this example which is found with Asp.net Ajax Documentation with the Custom executor. The following shows the code of this custom executor.

Type.registerNamespace('Sys.Net');

Sys.Net.XMLHttpSyncExecutor = function()
{
    Sys.Net.XMLHttpSyncExecutor.initializeBase(this);

    this._started = false;
    this._responseAvailable = false;

    this._onReceiveHandler = null;

    this._responseData = null;
    this._statusCode = null;
    this._statusText = null;
    this._headers = null;
}

Sys.Net.XMLHttpSyncExecutor.prototype =
{
    get_aborted : function()
    {
        if (arguments.length !== 0) throw Error.parameterCount();

        return false;
    },

    get_responseAvailable : function()
    {
        if (arguments.length !== 0) throw Error.parameterCount();

        return this._responseAvailable;
    },

    get_responseData : function()
    {
        if (arguments.length !== 0) throw Error.parameterCount();

        if (!this._responseAvailable)
        {
            throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'get_responseData'));
        }

        return this._responseData;
    },

    get_started : function()
    {
        if (arguments.length !== 0) throw Error.parameterCount();

        return this._started;
    },

    get_statusCode : function()
    {
        if (arguments.length !== 0) throw Error.parameterCount();

        if (!this._responseAvailable)
        {
            throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'get_statusCode'));
        }

        return this._statusCode;
    },

    get_statusText : function()
    {
        if (arguments.length !== 0) throw Error.parameterCount();

        if (!this._responseAvailable)
        {
            throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'get_statusText'));
        }

        return this._statusText;
    },

    get_xml : function()
    {
        if (arguments.length !== 0) throw Error.parameterCount();

        if (!this._responseAvailable)
        {
            throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'get_xml'));
        }

        var xml = this._responseData;

        if ((!xml) || (!xml.documentElement))
        {
            xml = new XMLDOM(this._responseData);

            if ((!xml) || (!xml.documentElement))
            {
                return null;
            }
        }
        else if (navigator.userAgent.indexOf('MSIE') !== -1)
        {
            xml.setProperty('SelectionLanguage', 'XPath');
        }

        if ((xml.documentElement.namespaceURI === "http://www.mozilla.org/newlayout/xml/parsererror.xml") &&
            (xml.documentElement.tagName === "parsererror"))
        {
            return null;
        }

        if (xml.documentElement.firstChild && xml.documentElement.firstChild.tagName === "parsererror")
        {
            return null;
        }

        return xml;
    },

    executeRequest : function()
    {
        if (arguments.length !== 0) throw Error.parameterCount();

        if (this._started)
        {
            throw Error.invalidOperation(String.format(Sys.Res.cannotCallOnceStarted, 'executeRequest'));
        }

        var webRequest = this.get_webRequest();

        if (webRequest === null)
        {
            throw Error.invalidOperation(Sys.Res.nullWebRequest);
        }

        var body = webRequest.get_body();
        var headers = webRequest.get_headers();
        var verb = webRequest.get_httpVerb();

        var xmlHttpRequest = new XMLHttpRequest();
        this._onReceiveHandler = Function.createCallback(this._onReadyStateChange, {sender:this, xmlHttp:xmlHttpRequest});
        this._started = true;
        xmlHttpRequest.onreadystatechange = this._onReceiveHandler;
        xmlHttpRequest.open(verb, webRequest.getResolvedUrl(), false); // False to call Synchronously

        if (headers)
        {
            for (var header in headers)
            {
                var val = headers[header];

                if (typeof(val) !== "function")
                {
                    xmlHttpRequest.setRequestHeader(header, val);
                }
            }
        }

        if (verb.toLowerCase() === "post")
        {
            if ((headers === null) || !headers['Content-Type'])
            {
                xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            }

            if (!body)
            {
                body = '';
            }
        }

        xmlHttpRequest.send(body);
    },

    getAllResponseHeaders : function()
    {
        if (arguments.length !== 0) throw Error.parameterCount();

        if (!this._responseAvailable)
        {
            throw Error.invalidOperation(String.format(Sys.Res.cannotCallBeforeResponse, 'getAllResponseHeaders'));
        }

        return this._headers;
    },

    _onReadyStateChange : function(e)
    {
        if (e.xmlHttp.readyState === 4)
        {
            e.sender._responseAvailable = true;
            e.sender._responseData = e.xmlHttp.responseText;
            e.sender._statusCode = e.xmlHttp.status;
            e.sender._statusText = e.xmlHttp.statusText;
            e.sender._headers = e.xmlHttp.getAllResponseHeaders();

            e.xmlHttp.onreadystatechange = Function.emptyMethod;
            e.sender._onReceiveHandler = null;

            e.sender._started = false;
        }
    }
}

Sys.Net.XMLHttpSyncExecutor.registerClass('Sys.Net.XMLHttpSyncExecutor', Sys.Net.WebRequestExecutor);

if (typeof(Sys) != 'undefined')
{
    Sys.Application.notifyScriptLoaded();
}

You can also download the full code listing from this link. Since the nature of the Call is Synchronous we can call it Synchronous JavaScript and XML or SJAX.

kick it on DotNetKicks.com

Posted on Wednesday, July 4, 2007 6:57 PM Asp.net Ajax , Tips/Tricks | Back to top


Comments on this post: SJAX Call

# re: SJAX Call
Requesting Gravatar...
Hi!

I have a problem with this code. When i use it, i got the text from the .aspx file, not the content...
So if my getTarget.aspx contains a "Hello world!" text i get this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
Untitled Page
</title></head>
<body>
<form name="form1" method="post" action=" getTarget.aspx" id="form1">
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGSo0F+mcOYWKuTxz5wzJ04Pvl9xjA==" />
</div>

<div id="textx">
Hello! This is my test webpage!
</div>
</form>
</body>
</html>

Please help me!

Thank you!






Left by Paniti Marta on Aug 21, 2007 2:18 PM

# Thanks for the great insight!
Requesting Gravatar...
Hi Amit

I just wanted to let you know that this post as a basis for getting all generated web service proxy calls to execute synchronously (see http://ridgway.co.za/archive/2007/10/30/using-synchronous-asp.net-ajax-web-service-calls-and-scriptaculous-to.aspx). This is useful when doing JavaScript unit testing.

Thanks for the insight and sharing!

King Regards
Eden
Left by Eden Ridgway on Oct 30, 2007 12:24 PM

# re: SJAX Call
Requesting Gravatar...
Hi, this solution doesn't work with firefox.
the strange is that it works only if I install tha add-on firebug...

bye
Left by emiliano on Mar 04, 2008 3:01 PM

# re: SJAX Call
Requesting Gravatar...
Hi Amit!

Thanks for your code!

However I have some problems to get it work in FF.
I'm not an expert in JS and AJAX, but debugging brought me to conclusion that something doesn't work properly in your code when you create callbacks in

var xmlHttpRequest = new XMLHttpRequest();
this._onReceiveHandler = Function.createCallback(this._onReadyStateChange, {sender:this, xmlHttp:xmlHttpRequest});
this._started = true;
xmlHttpRequest.onreadystatechange = this._onReceiveHandler;

_onReceiveHandler is never called in FF.
It might be problem in MS createCallback code as well. Unfortunately I cannot fix it myself.

Could you be so kind and take a look at this problem? Please!
Left by CNemo on Apr 24, 2008 8:42 PM

# Another approach to the same problem
Requesting Gravatar...
You can also get validators and web services working together by making the validator asynchronous instead of making the web service synchronous.

http://www.tqcblog.com/archive/2008/06/18/asynchronous-validation-with-asp-net-ajax-customvalidator-and-web-services.aspx
Left by Tom Clarkson on Jun 19, 2008 7:34 AM

Your comment:
 (will show your gravatar)


Copyright © Kazi Manzur Rashid | Powered by: GeeksWithBlogs.net | Join free