Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

ExtJS and .NET WebServices, remote domain access

4.82/5 (5 votes)
13 Jul 2009CPOL3 min read 43.8K   617  
Remote access to .NET Web Services using ExtJS.

Introduction

In my last article, I showed how easy it is access to a .NET Web Service from the ExtJS JavaScript library. This solution has one big disadvantage. Due to security reasons, the HTTPRequest method used for performing AJAX calls is restricted to in-domain calls only. What if our Web Service is located on a different machine and a different domain? You will find an answer for this question in this article.

Solution

As I mentioned before, it impossible to access data located in a different domain using the HTTPRequest method, but we can use a trick. It is possible, using JavaScript, to add any HTML tag to your current document - even a SCRIPT tag. As all we know, the SCRIPT tag allows to include JavaScript code into a document and also the SCRIPT tag can have a URL where JavaScript code is located. This is not limited to the same domain, like in the HTTPRequest case. Let's imagine that our source code in this case is remotely and dynamically produced data returned as valid JavaScript code. When the document is ready, we can access this data and that's all. There are two drawbacks to this solution. Performance is slightly worse (we need to call a remote domain, etc.), and we have to prepare the server side to handle script-tag requests. On the ExtJS side, we can just use the ScriptTagProxy object class instead of the regular HttpProxy.

Let's take data from the last article:

JavaScript
{ "d": [{"AgentID": 1, "FirstName": "Jelena", 
  "LastName": "Akerhus"},{"AgentID": 2, 
  "FirstName": "Londo", "LastName": "Molari"}]}

To make it to work with the ExtJS ScriptTagProxy class, we need to return data in a format like this:

JavaScript
stcCallback001({ "d": [{"AgentID": 1, 
  "FirstName": "Jelena", "LastName": "Akerhus"}, 
  {"AgentID": 2, "FirstName": "Londo", 
  "LastName": "Molari"}]});

Hey, does it look like a JavaScript function call with our data provided as a parameter? Yes, exactly! The name for the callback function is provided by the ScriptTagProxy class as a parameter added to the URL. The default name for this callback is... callback. The URL to access the remote data source will look like this:

http://your-remoted-domain/ScriptTagProxyHandler?callback=stcCallback001

ScriptTagProxy will do the job to extract data and put it into the store. All we need is to specify ScriptTagProxy instead of HttpProxy. Let's take the code from the last article and change the proxy.

JavaScript
var store = new Ext.data.JsonStore({ 
    autoLoad: true, 
    proxy: new Ext.data.ScriptTagProxy({ 
        url: 'http://your-remote-domain/ScriptTagProxyHandler', 
    }), 
    root: 'd', 
    id: 'Id', 
    fields: ['Id', 'FirstName', 'LastName', 'BirthDate'] 
});

Now we need to figure out how we can make our Web Service work with ScriptTagProxy. It could be easily achieved with a solution called PageProxy. What is PageProxy? In simple words, it is an ASPX web page which will receive all requests, and using Reflection, forward them to a Web Service; then it will get back the response, which is an enclosure with the JavaScript callback function and forward it to the request's sender.

First, we need to create an ASPX web page; let's name it ScriptPageProxy.aspx. To keep compatibility with the Web Service GET method call, we want to call Web Services with the name of the method specified as last part of the URL and then catch the parameters sent in the Request object.

C#
public partial class SimpleScriptPageProxy : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        StringBuilder sb = new StringBuilder();
        string result = "";
        string callback = "";

        try
        {
            // Get method name in web service GET call format
            string methodName = Request.PathInfo.Replace("/", "");
            // Get name of callback function
            callback = Request["callback"].ToString();

            // Invoke web service method and serialize response to json
            // !Important!: Service is type of our web service
            result = jsonSerialize(invokeMethod(typeof(Service), methodName));
        }
        catch (Exception ex)
        {
            result = jsonSerialize(ex);
            // When anything wrong happens,
            // just return exception serialized to json
        }
        // Prepare and send back response
        sb.AppendFormat("{0}({{ d: {1} }});", callback, result);
        Response.Write(sb.ToString());

    }

To help with this task, there are two helper methods. First, we call a Web Service method using its type and method name. The other is just any .NET object serialization to JSON format.

C#
/// <summary>
/// This method invokes fn method on type Type and returns answer.
/// </summary>
/// <param name="type">Type of class with method</param>
/// <param name="fn">Function name</param>
/// <returns>Method result</returns>
private object invokeMethod(Type type, string fn)
{
    object result;
    object instance = Activator.CreateInstance(type);
    MethodInfo method = type.GetMethod(fn);
    ParameterInfo[] parameters = method.GetParameters();

    if (parameters.Length == 0)
    {
        result = method.Invoke(instance, null);
    }
    else
    {
        List<string> paramValues = new List<string>();
        for (int i = 0; parameters.Length > i; i++)
        {
            string paramName = parameters[i].Name;
            // Get parameters directly from Request object
            string paramValue = Request[paramName];

            paramValues.Add(paramValue);
        }
        result = method.Invoke(instance, paramValues.ToArray());
    }
    return result;
}

// Helper method for json serialization
private string jsonSerialize(object o)
{
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    return serializer.Serialize(o);
}

When the ScriptPageProxy page is ready, all we need is to know how to call it. In the JsonStore declaration, specify the URL to this page. If we consider www.remote.com as our domain, it will look like this:

JavaScript
var store = new Ext.data.JsonStore({ 
    autoLoad: true, 
    proxy: new Ext.data.ScriptTagProxy({ 
        url: 'http://www.remote.com/ScriptPageProxy.aspx/GetPeople', 
    }), 
    root: 'd', 
    id: 'Id', 
    fields: ['Id', 'FirstName', 'LastName', 'BirthDate'] 
}); 

If you like to send any parameters to the Web Service, you can send them with a request using a GET or POST method. For example, to send a parameter with the name Category and value Financial:

JavaScript
var store = new Ext.data.JsonStore({ 
    autoLoad: true, 
    proxy: new Ext.data.ScriptTagProxy({ 
        url: 'http://www.remote.com/ScriptPageProxy.aspx/GetPeople', 
    }), 
    baseParams: { Category: 'Financial' },
    root: 'd', 
    id: 'Id', 
    fields: ['Id', 'FirstName', 'LastName', 'BirthDate'] 
});

Source code

The attached source code contains an additional project which allows you to create a ScriptPageProxy in a very easy way. All you need is to create is a new ASPX page, change the parent class to ScriptPage, and describe it with ScriptPageAttribute to show what the target Web Service class is. Everything is available as source code, feel free to send any questions and comments regarding this class.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)