Posted on 02/10/2011 by FlashInvader views 110 Home 

Click Here to see a working sample!

Well here we are after 20 years of using HTML and we still don't  have a universal way to consume web service cross domain (XXS) without being prompted by the browser that we are about to connect to another domain and whilst HTML5 offers web-sockets, I've yet to see a working cross browser sample and the only way I know around this unless you want to play with Flash is to pass outgoing message in the URL of the source for a script file and to then dynamically generate JavaScript on the fly to return the results.

<script type="text/javascript" src="http://www.CrossDomain.com/Json.js?Arg1=123&Arg2=456"></script>

So OK it works but for this to work you need full control over both domains and also need to install code on the remote server to deal with the script request/response and frankly I quite like the simplicity of static web-methods in ASPX pages and it should be quite easy to persuade someone to share a web-method you don't know so long as you don't need to upload code to run on their server.

JQuery $.ajax({ type: "post", url: "www.someaddress.com' ,DataType: "json",.. Callback function}

XMLHttpRequest for  Internet Explorer  8/9 and Msxml2.XMLHTTP for Firefox are both simple to use and JQuery offers a wrapper  ($.Ajax function as shown above) but I like to stay in control and find the size of JQuery is a little too big for my liking and objects returned from an Aspx web-method are wrapped up in an outer object 'd'  plus I want cross domain access.

Json String =   {"d":"{firstname: \"Dave\u0027s\",lastname: \"String-Smith\"}"}

Json is much lighter weight than XML packets (Soap) that bulk out the data and despite the name ' XMLHttpRequest' you will be happy to know that setting the content type to Json ensure the HTTP request size is kept to a minimum and only contains the Json string

XmlHttp.setRequestHeader("Content-Type", "application/json");

On the server side we have easy to use web-methods and you can place these in any aspx page as static methods so long as you remember to include " using System.Web.Services;" at the top of the page.

[WebMethod]
    public static object  GetObject(string data)
    { //Best way is to return object data as Json string.
        var  response = new { firstname = data, lastname = "Object-Jones" };
        return response;
    } 

But what about cross domain ! Well since it's so easy to write web-methods then it didn't take much thinking to realise that a web-method could be used on our server to relay request from a browser to another server without having to worry about security warnings from the browser .

Cross domain Json

Calling a local web method or a remote method using the script file I developed is almost exactly the same and as usual call-back function are use to receive the objects that are returned.

var PageName ="http ://localhost/YourSite/Json.aspx/"; //You need to edit this

SendJsonData (MyCallBackFunction, PageName + "GetStringObject", "{data: \"Dave's\"}")
RelayJsonData(MyCallBackFunction, PageName + "GetStringObject", "{data: \"Dave's\"}")

 function MyCallBackFunction(Obj, Status) {
         //Display a person object returned from the Aspx.WebMethod
        alert(Obj.firstname + " " + Obj.lastname + " " + Status);
    }

All you need to get the above example working is to include a small JavaScript file in the HTML page and to place a static Web-Method in one of the aspx page as shown below on the server and you are ready to rock and roll.

 [WebMethod]
public static string RelayJson(string Url, string Data)
    {
        Data =System.Web.HttpUtility.UrlDecode(Data);
        Url = System.Web.HttpUtility.UrlDecode(Url);
        string HTML = GetWebMethod(Url, Data).Replace("\\\"", "\"").Replace("\\\\", "\\");
        string Padding = "000";
        for (int f = 1; f <= 256; f++)
        { //E.G Replace \u0027 with &#39;
            string Hex=\\u + Padding.Substring(0,4-f.ToString().Length) + f;
            string Dec = "&#" + Int32.Parse(f.ToString(), NumberStyles.HexNumber) + ";";
            HTML = HTML.Replace(Hex, Dec);
        }
        HTML = System.Web.HttpUtility.HtmlDecode(HTML);

        if (HTML.StartsWith("{\"d\":") && HTML.EndsWith("}}"))
            HTML = HTML.Substring(5,HTML.Length-6); // Json string
        else if (HTML.StartsWith("{\"d\":") && HTML.EndsWith("}\"}"))
            HTML = HTML.Substring(6, HTML.Length - 8); // Object
        else if (HTML.StartsWith("{\"d\":") && HTML.EndsWith("\"}"))
            HTML =HTML.Substring(6, HTML.Length - 8); // Simple string
        return HTML;
    }

I've highlighted the section in Yellow to raise a point that Json strings are Unicode encoded so that "Tim's" become "Tim\u0027s" and this needs to be converted before being sent back out again but for the life of me I could not find anything that worked in System.Web.HttpUtility, System.Text.Encoding or Convert??? that would do the job and did not want to use ' JSON Serialization' since many sites in the real world are yet to catch up with Dot.Net framework 4 so I ended up converting Json to HTML by converting hex number to decimal and escaping them and then decoding the HTML to get the data I needed.

Please offer a solution if you have one but also note that hard coding a string like

string Data = "\u003cb\u003emoney\u003c/b\u003e/markets/article-2015767 \u003cb\u003e...\u003c/b\u003e";

And testing it does not solve the problem because if you add a break point and inspect the data then you will notice the data is already converted just to make life difficult.

WARNING Don't be tempted to hardcode Url's for the Home-Address ! if your site can be accessed with or without the 'WWW' and a Json request from Internet Explorer  is made and includes /omits the 'WWW' at the wrong time then your XMLHttpRequest will throw an error of 'Access Denied'

if you just want a lightweight JavaScript wrapper to call a local web-method then simply  cut/copy/paste the code shown at the bottom of the page and use the ' SendJsonData' function as documented above or click Here to download a small project that includes test code and the aspx page containing the relay web-method.

The Code listed below is now a bit out of date and has been extended in the release version !

var JsonXmlHttp;
var JsonCallBack
function RelayJsonData(CallBack, Url, Data) {
    //Relay call via our server WebMethod to the remote aspx WebMethod
    SendJsonData(CallBack, "http://localhost/YourSite/Json.aspx/RelayJson", "{Url: \"" + escape(Url) + "\",Data: \"" + escape(Data) + "\"}");
} // See above warning in red, too late to fix here but has been fixed in the relased code

function SendJsonData(CallBack, Url, Data) {
    JsonCallBack = CallBack;
    if (window.XMLHttpRequest)
        JsonXmlHttp = new XMLHttpRequest(); //IE6 +
    else if (window.ActiveXObject)
        JsonXmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); //FireFox
    else
        JsonCallBack("Can not connect", 404);
    JsonXmlHttp.open("post", Url, true);
    JsonXmlHttp.setRequestHeader("CharSet", "UTF-8");
    JsonXmlHttp.setRequestHeader("Content-Type", "application/json");
    JsonXmlHttp.setRequestHeader("IsLookup", "true");
    JsonXmlHttp.onreadystatechange = JsonCallBackFunction;
    JsonXmlHttp.send(Data); //Data is not bulked out as xml in HTTP request !
}

function JsonCallBackFunction() {
    if (JsonXmlHttp.readyState == 4) {
        var Obj = ConvertToJson(JsonXmlHttp.responseText)
        JsonCallBack(Obj, JsonXmlHttp.status);
    }
    else if (JsonXmlHttp.readyState == 2 && JsonXmlHttp.status > 400) {
        JsonCallBack(JsonXmlHttp.responseText, JsonXmlHttp.status);
        JsonXmlHttp.abort(); //Stop on error but return error message
    }
}

function ConvertToJson(data) {
    if (window.JSON && window.JSON.parse)
        return GetInnerObject(window.JSON.parse(data)); //We can do it the easy way!
    var rvalidchars = /^[\],:{}\s]*$/,
     rvalidescape = /\\(?:["
\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
     rvalidtokens = /"[^"
\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
     rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g

    if (rvalidchars.test(data.replace(rvalidescape, "@").replace(rvalidtokens, "]").replace(rvalidbraces, "")))
        return GetInnerObject(eval("(" + data + ");"));
    return data; //Don't know what this is
}

function GetInnerObject(Obj) {
    //Data returned as {"d":{"firstname":"Tim","lastname":"Davis"}} from Aspx.WebMethod
    if (typeof (Obj.d) == "object")
        return Obj.d; //Return object 'D'
    else if (typeof (Obj.d) == "string" && Obj.d[0] != '{' && Obj.d.indexOf(":") == -1 && Obj.d.indexOf('"') == -1)
        return Obj.d; //Return simple string 'D'
    else return eval("(" + Obj.d + ");"); //Convert and return 'D'
}

 


Leave a comment
 Name
 Email Address  Will not be published
 Website