-
9 Nov 2007 1:09 AM #1
Cross-domain Ext.Ajax/Ext.data.Connection
Cross-domain Ext.Ajax/Ext.data.Connection
Here, I've extended Ext.data.Connection (and therefore Ext.Ajax, because that is an instance of Ext.data.Connection) to retrieve data, and pass it to your callback function from another domain.
Of course, since it uses a script tag, the server-side script you invoke must wrap the returned data in a call to a callback function. So text must be passed back as a quoted string parameter to a function.
Also, the method is always GET, so all parameters are sent on the URL.
HTTP headers cannot be set on the request, or read from the response.
It creates a bogus XHR containing responseText and responseXML by parsing the resulting data object.
I have successfully retrieved an XML document through this. The servlet quoted the string according to javascript conventions, and wrapped it thus:
It still needs some work on aborting transactions because the abort code in Ext.data.Connection assumes XHR transport. I can fix that to test what the "trans" object looks like and do different things. But this works:Code:w = response.getWriter(); w.write(callback + "('"); byte[] buffer = getReportXML(); w.write(Utils.javascriptString(buffer.toString())); w.write("');");
Here is the override:Code:Ext.Ajax.request({ url: 'http://localhost:8080/aspicio/getxml.do', params: { listId: 'CountryListManager141Grid509', format: 'xml' }, scriptTag: true, // Use script tag transport success: function(r) { console.log(r); } });
Code:Ext.lib.Ajax.isCrossDomain = function(u) { var match = /(?:(\w*:)\/\/)?([\w\.]*(?::\d*)?)/.exec(u); if (!match[1]) return false; // No protocol, not cross-domain return (match[1] != location.protocol) || (match[2] != location.host); }; Ext.override(Ext.data.Connection, { request : function(o){ if(this.fireEvent("beforerequest", this, o) !== false){ var p = o.params; if(typeof p == "function"){ p = p.call(o.scope||window, o); } if(typeof p == "object"){ p = Ext.urlEncode(p); } if(this.extraParams){ var extras = Ext.urlEncode(this.extraParams); p = p ? (p + '&' + extras) : extras; } var url = o.url || this.url; if(typeof url == 'function'){ url = url.call(o.scope||window, o); } if(o.form){ var form = Ext.getDom(o.form); url = url || form.action; var enctype = form.getAttribute("enctype"); if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){ return this.doFormUpload(o, p, url); } var f = Ext.lib.Ajax.serializeForm(form); p = p ? (p + '&' + f) : f; } var hs = o.headers; if(this.defaultHeaders){ hs = Ext.apply(hs || {}, this.defaultHeaders); if(!o.headers){ o.headers = hs; } } var cb = { success: this.handleResponse, failure: this.handleFailure, scope: this, argument: {options: o}, timeout : this.timeout }; var method = o.method||this.method||(p ? "POST" : "GET"); if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){ url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime()); } if(typeof o.autoAbort == 'boolean'){ // options gets top priority if(o.autoAbort){ this.abort(); } }else if(this.autoAbort !== false){ this.abort(); } if((method == 'GET' && p) || o.xmlData || o.jsonData){ url += (url.indexOf('?') != -1 ? '&' : '?') + p; p = ''; } if (o.scriptTag || this.scriptTag || Ext.lib.Ajax.isCrossDomain(url)) { this.transId = this.scriptRequest(method, url, cb, p, o); } else { this.transId = Ext.lib.Ajax.request(method, url, cb, p, o); } return this.transId; }else{ Ext.callback(o.callback, o.scope, [o, null, null]); return null; } }, scriptRequest : function(method, url, cb, data, options) { var transId = ++Ext.data.ScriptTagProxy.TRANS_ID; var trans = { id : transId, cb : options.callbackName || "stcCallback"+transId, scriptId : "stcScript"+transId, options : options }; url += (url.indexOf("?") != -1 ? "&" : "?") + data + String.format("&{0}={1}", options.callbackParam || this.callbackParam || 'callback', trans.cb); var conn = this; window[trans.cb] = function(o){ conn.handleScriptResponse(o, trans); }; // Set up the timeout handler trans.timeoutId = this.handleScriptFailure.defer(cb.timeout, this, [trans]); var script = document.createElement("script"); script.setAttribute("src", url); script.setAttribute("type", "text/javascript"); script.setAttribute("id", trans.scriptId); document.getElementsByTagName("head")[0].appendChild(script); return trans; }, handleScriptResponse : function(o, trans){ this.transId = false; this.destroyScriptTrans(trans, true); var options = trans.options; // Attempt to parse a string parameter as XML. var doc; if (typeof o == 'string') { if (window.ActiveXObject) { doc = new ActiveXObject("Microsoft.XMLDOM"); doc.async = "false"; doc.loadXML(o); } else { doc = new DOMParser().parseFromString(o,"text/xml"); } } // Create the bogus XHR response = { responseObject: o, responseText: (typeof o == "object") ? Ext.util.JSON.encode(o) : String(o), responseXML: doc, argument: options.argument } this.fireEvent("requestcomplete", this, response, options); Ext.callback(options.success, options.scope, [response, options]); Ext.callback(options.callback, options.scope, [options, true, response]); }, handleScriptFailure: function(trans) { this.transId = false; this.destroyScriptTrans(trans, false); var options = trans.options; response = { argument: options.argument, status: 500, statusText: 'Server failed to respond', responseText: '' }; this.fireEvent("requestexception", this, response, options, { status: -1, statusText: 'communication failure' }); Ext.callback(options.failure, options.scope, [response, options]); Ext.callback(options.callback, options.scope, [options, false, response]); }, // private destroyScriptTrans : function(trans, isLoaded){ document.getElementsByTagName("head")[0].removeChild(document.getElementById(trans.scriptId)); clearTimeout(trans.timeoutId); if(isLoaded){ window[trans.cb] = undefined; try{ delete window[trans.cb]; }catch(e){} }else{ // if hasn't been loaded, wait for load to remove it to prevent script error window[trans.cb] = function(){ window[trans.cb] = undefined; try{ delete window[trans.cb]; }catch(e){} }; } } });
-
9 Nov 2007 4:00 AM #2
Cool!
Cool!
Hi!
You are the greatest
I've needed to create SSL authentication request in my application and I think that I've found solution
-
9 Nov 2007 5:16 AM #3
It's a bit more code, but if Ext.data.Connection were updated to include this capability, then SctiptTagProxy would be very small. It would just call Ext.Ajax.request with scriptTag:true
-
9 Nov 2007 5:24 AM #4
Thanks Animal,
Is posible a small live example?
Excellent work!!
-
9 Nov 2007 5:48 AM #5
I don't have a website.
-
9 Nov 2007 5:49 AM #6
Actually, I could just drop something into here which will drop into examples/form.... hang on...
-
9 Nov 2007 5:59 AM #7
OK, drop this into examples/<whatever>
Code:<html> <head> <title>Script Tag Ajax</title> <!-- LIBS --> <script type="text/javascript" src="../../adapter/ext/ext-base.js"></script> <!-- ENDLIBS --> <script type="text/javascript" src="../../ext-all.js"></script> <script type="text/javascript"> Ext.override(Ext.data.Connection, { request : function(o){ if(this.fireEvent("beforerequest", this, o) !== false){ var p = o.params; if(typeof p == "function"){ p = p.call(o.scope||window, o); } if(typeof p == "object"){ p = Ext.urlEncode(p); } if(this.extraParams){ var extras = Ext.urlEncode(this.extraParams); p = p ? (p + '&' + extras) : extras; } var url = o.url || this.url; if(typeof url == 'function'){ url = url.call(o.scope||window, o); } if(o.form){ var form = Ext.getDom(o.form); url = url || form.action; var enctype = form.getAttribute("enctype"); if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){ return this.doFormUpload(o, p, url); } var f = Ext.lib.Ajax.serializeForm(form); p = p ? (p + '&' + f) : f; } var hs = o.headers; if(this.defaultHeaders){ hs = Ext.apply(hs || {}, this.defaultHeaders); if(!o.headers){ o.headers = hs; } } var cb = { success: this.handleResponse, failure: this.handleFailure, scope: this, argument: {options: o}, timeout : this.timeout }; var method = o.method||this.method||(p ? "POST" : "GET"); if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){ url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime()); } if(typeof o.autoAbort == 'boolean'){ // options gets top priority if(o.autoAbort){ this.abort(); } }else if(this.autoAbort !== false){ this.abort(); } if((method == 'GET' && p) || o.xmlData || o.jsonData){ url += (url.indexOf('?') != -1 ? '&' : '?') + p; p = ''; } if (o.scriptTag) { this.transId = this.scriptRequest(method, url, cb, p, o); } else { this.transId = Ext.lib.Ajax.request(method, url, cb, p, o); } return this.transId; }else{ Ext.callback(o.callback, o.scope, [o, null, null]); return null; } }, scriptRequest : function(method, url, cb, data, options) { var transId = ++Ext.data.ScriptTagProxy.TRANS_ID; var trans = { id : transId, cb : options.callbackName || "stcCallback"+transId, scriptId : "stcScript"+transId, options : options }; url += (url.indexOf("?") != -1 ? "&" : "?") + data + String.format("&{0}={1}", options.callbackParam || 'callback', trans.cb); var conn = this; window[trans.cb] = function(o){ conn.handleScriptResponse(o, trans); }; // Set up the timeout handler trans.timeoutId = this.handleScriptFailure.defer(cb.timeout, this, [trans]); var script = document.createElement("script"); script.setAttribute("src", url); script.setAttribute("type", "text/javascript"); script.setAttribute("id", trans.scriptId); document.getElementsByTagName("head")[0].appendChild(script); return trans; }, handleScriptResponse : function(o, trans){ this.transId = false; this.destroyScriptTrans(trans, true); var options = trans.options; // Attempt to parse a string parameter as XML. var doc; if (typeof o == 'string') { if (window.ActiveXObject) { var doc = new ActiveXObject("Microsoft.XMLDOM"); doc.async = "false"; doc.loadXML(o); } else { var doc = new DOMParser().parseFromString(o,"text/xml"); } } // Create the bogus XHR response = { responseObject: o, responseText: (typeof o == "object") ? Ext.util.JSON.encode(o) : String(o), responseXML: doc, argument: options.argument } this.fireEvent("requestcomplete", this, response, options); Ext.callback(options.success, options.scope, [response, options]); Ext.callback(options.callback, options.scope, [options, true, response]); }, handleScriptFailure: function(trans) { this.trans = false; this.destroyScriptTrans(trans, false); var options = trans.options; response = { argument: options.argument }; this.fireEvent("requestexception", this, response, options, new Error("Timeout")); Ext.callback(options.failure, options.scope, [response, options]); Ext.callback(options.callback, options.scope, [options, false, response]); }, // private destroyScriptTrans : function(trans, isLoaded){ document.getElementsByTagName("head")[0].removeChild(document.getElementById(trans.scriptId)); clearTimeout(trans.timeoutId); if(isLoaded){ window[trans.cb] = undefined; try{ delete window[trans.cb]; }catch(e){} }else{ // if hasn't been loaded, wait for load to remove it to prevent script error window[trans.cb] = function(){ window[trans.cb] = undefined; try{ delete window[trans.cb]; }catch(e){} }; } } }); Ext.onReady(function() { Ext.Ajax.request({ url: 'http://extjs.com/forum/topics-remote.php', scriptTag: true, params: { query: 'Animal', limit: 10, start: 0 }, success: function(response) { Ext.get("json-result").update(response.responseText); } }); }); </script> </head> <body> <h1>Request data from the ExtJs forum search service</h1> <h3>Code:</h3> <code><pre> Ext.Ajax.request({ url: 'http://extjs.com/forum/topics-remote.php', scriptTag: true, params: { query: 'Animal', limit: 10, start: 0 }, success: function(response) { Ext.get("json-result").update(response.responseText); } }); </pre></code> <h3>Response:</h3> <code><pre id="json-result"></pre></code> </body> </html>
-
9 Nov 2007 9:06 AM #8
Excellent example!
Thanks!!
-
15 Nov 2007 12:18 AM #9
-
15 Nov 2007 7:54 AM #10
The browser is trying to process valid javascript source within the generated <script> tag. You're loading raw XML into it, thus failing.
If you have some level of control of the cross-domain server response, the response will have to be scripted to pass your XML to the callback function you should have specified with the callback parameter in the request."be dom-ready..."
Doug Hendricks
Maintaining ux: ManagedIFrame, MIF2 (FAQ, Wiki), ux.Media/Flash, AudioEvents, ux.Chart[Fusion,OFC,amChart], ext-basex.js/$JIT, Documentation Site.
Got Sencha licensing questions? Find out more here.


Reply With Quote

