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:
{ "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:
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.
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.
public partial class SimpleScriptPageProxy : Page
{
protected void Page_Load(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
string result = "";
string callback = "";
try
{
string methodName = Request.PathInfo.Replace("/", "");
callback = Request["callback"].ToString();
result = jsonSerialize(invokeMethod(typeof(Service), methodName));
}
catch (Exception ex)
{
result = jsonSerialize(ex);
}
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.
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;
string paramValue = Request[paramName];
paramValues.Add(paramValue);
}
result = method.Invoke(instance, paramValues.ToArray());
}
return result;
}
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:
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:
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.