Introduction - The Cross Domain Query Challenge
The promise of SOA (Search Oriented Architecture) is to share. Mid-tier SOA requires extra coding and causes an extra data hop. Client side browser based SOA requests are limited because the JavaScript XMLHttpRequest
(XHR) does not support cross domain queries at this time.
Flash and Silverlight API options are available, but have three drawbacks:
- They must be installed for the query to work.
- They take time to load, delaying the query.
- The code can take a while to write, test, and re-test with every version, browser, etc.
In 2009, the most common solution for cross domain queries is what the major ad networks use: dynamic JavaScript generation. This is sometimes mistakenly called cross domain JSON, cross domain XML, and several other things. The format is not the point. The connectivity is that you can load JavaScript libraries cross domain, and that ASP.NET can dynamically generate custom content for each JavaScript library call.
Code to Write Code - Client and Server
Generally speaking, it is poor form to write code that writes code. So, if you want textbook, architectural perfection, please stop reading. If you want a pragmatic, widely implemented solution, read on.
Below is an example client side JavaScript code for calling ASPX to generate a script library specific to a passed value:
<!---->
<!---->
<input id="SpreetySearchTextHidden" value="Scooby-Doo" type="hidden" />
<script type="text/javascript"><!--//<![CDATA[
try {
var spreetySearch = document.getElementById( if (spreetySearch != "") {
document.write(String.fromCharCode(60, 83, 67, 82, 73, 80, 84));
document.write( Scripts2009/TheCodeProjectExampleByName.aspx?query= spreetySearch + document.write(String.fromCharCode(60, 47, 83, 67, 82, 73, 80, 84, 62));
} else {
document.write("<p>Error: Please update the SpreetySearchTextHidden
field with a value.</p>"); }
} catch (err) { document.write("<p>Error: Unable to query Spreety.</p>"); }
//]]>--></script>
-->
Placement of the above code between the <BODY>
tags of an HTML document will render as follows:
Result from ASP.NET: Scooby-Doo
The client code calls a .ASPX page that looks like the following (simplified... production version has extensive error checking and utilizes common code libraries):
<%@ Page Language="C#" AutoEventWireup="true" %>
<%
try
{
string query = Request.QueryString.Get("query");
string result = "<p style='color:Blue'>Result from ASP.NET: <b>" +
query.Replace("\"", "\\\"") + "</b></p>";
Response.ContentType = "text/javascript";
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.AddHeader("type", "text/javascript");
Response.Write("document.write(\"" + result + "\");");
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
%>
The client JavaScript code calls ASPX code, which returns JavaScript code to execute on the client to write the result.
Client Code - Line By Line
In the client side JavaScript code, there is:
A hidden field with the value "Scooby-Doo
". Feel free to change that value as desired.
<input id="SpreetySearchTextHidden" value="Scooby-Doo" type="hidden" />
The start of a script to write a script. The comment is to avoid displaying code if JavaScript is not enabled. The CDATA
tells the parser to interpret as only character data.
<script type="text/javascript"><!--
Initiate error catching.
try {
Capture the hidden field's value into a JavaScript variable.
var spreetySearch = document.getElementById('SpreetySearchTextHidden').value;
Only remote call if something is being searched for.
if (spreetySearch != "") {
Here's where a script writes a script. (60, 83, 67, 82, 73, 80, 84) = <script> and (60, 47, 83, 67, 82, 73, 80, 84, 62) = ></script>. The src equals an ASPX page with a query parameter passed.
document.write(String.fromCharCode(60, 83, 67, 82, 73, 80, 84));
document.write(' src="http://ws.spreety.com/Scripts2009/
TheCodeProjectExampleByName.aspx?query=' +
spreetySearch + '" type="text/javascript">');
document.write(String.fromCharCode(60, 47, 83, 67, 82, 73, 80, 84, 62));
The rest of the JavaScript lines are informational or error handling.
ASPX Code - Line By Line
In the server side ASPX C# code, there is:
Define page header language and wire up.
<%@ Page Language="C#" AutoEventWireup="true" %>
Acquire the incoming query parameter.
string query = Request.QueryString.Get("query");
Process a result. To avoid new lines and carriage returns, utilize HTML markup. Also, double quotes need to be replaced with a \".
string result = "<p style='color:Blue'>Result from ASP.NET: <b>" +
query.Replace("\"", "\\\"") + "</b></p>";
Return the results as JavaScript to execute.
Response.ContentType = "text/javascript";
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.AddHeader("type", "text/javascript");
Write a line in C# to write a line of JavaScript code to write the results.
Response.Write("document.write(\"" + result + "\");");
Use with Caution
The main downside of cross domain JavaScript libraries is that unscrupulous folks could manipulate things. Oddly, the security sensitive rationale for preventing cross domain XMLHttpRequest
(XHR) resulted in programmers utilizing a greater security risk with cross domain script libraries. A second downside is the lack of error values returned. If your architect is concerned with the JavaScript option, please switch to Web Services, which can also be tested through the "Open APIs for Spreety" on their home page.
Summary
The promise of SOA remains obscured by implementation roadblocks. The cross domain scripting option is a common, effective work-around. While not architecturally perfect, the technique is fast, stable, and powers the multi-billion dollar ad industry today.
History
- 23rd October, 2009: Initial post