日历归档 |
|
<< < 2024 - 11 > >> | Su | Mo | Tu | We | Th | Fr | Sa | | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
|
|
|
About Me |
|
|
ZhangSichu |
|
Male |
|
32 |
|
ZhangSichu@gmail.com |
|
ZhangSichu@hotmail.com |
|
ZhangSichu.com |
|
weibo.com/zhangsichu |
|
|
|
个人推荐 |
|
|
|
|
分类归档 |
|
|
|
|
My Friends |
|
|
|
|
使用Script Request解决跨域请求问题
|
常用的Ajax框架,一般内部都封装了 XMLHttpRequest或者一个隐藏的Iframe来进行异步请求。XMLHttpRequest或者隐藏的Iframe是整个框架发出异步请求的引擎。但是由于浏览器对XMLHttpRequest和Iframe的安全限制,使得XMLHttpRequest只能请求本域内的资源。如果请求外域的资源,浏览器就会弹出提示或者错误。
IE中的提示:
FireFox中的错误:
使用隐藏的Iframe可以解决跨域请求的问题。可以顺畅的发出跨域请求,但是跨域请求后,Iframe一般会返回一段Javascript,例如:
<script type="text/javascript"> window.onload = function() { parent.frames[0].handleResponse( document.forms["formResponse"].result.value; ); } </script>
|
通过这段Javascript来操作Dom元素,调整UI界面。当隐藏的iframe请求的是同域的资源,这种调用不会有任何问题。但是当请求的是外域的资源时,浏览器会出错,Javascript不会被执行。 IE中的错误:
FireFox 会直接终止Javascript执行。没有错误提示。
在一些场景下很需要请求外域的资源,来绘制自己页面的Dom。例如请求google的Service取得搜索相关的信息。请求天气网站的Service取得天气情况。通过这些信息绘制自己页面的区域。
如何解决跨域请求成了关键问题。常规的做法是使用自己本域下的一个链接地址作为Proxy。先把请求发会给自己的服务器,自己的服务器再从服务器端请求指定的站点,服务器端得到返回后再返回给用户的浏览器。通过服务器做Proxy解决跨域请求的问题。
在Html的Dom元素中<script /> 标签没有跨域限制。可以使用script 元素进行跨域请求。它从外域返回的Javascript 可以自由的操作本页面中的Dom元素或者javascript对象。
测试Demo: 1. 修改自己的 host 添加了127.0.0.1 www.niceclient.com 让www.niceclient.com解析到自己。 host - %windir%\system32\drivers\etc
2. 在IIS中建立一个WebSite。设置主机头为www.niceclient.com 通过这样的设置建立一个自己机器上可以访问的www.niceclient.com。 这样可以使得自己机器上有两个可以控制的域。一个localhost一个www.niceclient.com。 3. 在www.niceclient.com下部署一个ashx ,模拟一个外域服务。 4. 在localhost中通过script访问www.niceclient.com的资源。
Test.html
<html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Script Request Test Page</title> <script type="text/javascript" src="ScriptRequest.js"></script> </head> <body> <script language="javascript" type="text/javascript"> function sendRequest(action) { var scriptRequest = new ScriptRequestSender(); scriptRequest.set_url(document.getElementById("tbxUrl").value); scriptRequest.set_body(document.getElementById("tbxBody").value); scriptRequest.set_header(action); scriptRequest.set_timeOut(3000); if(action == "queryText") { scriptRequest.add_completeCallback(onCompletedTextFormate); } else { scriptRequest.add_completeCallback(onCompletedXMLFormate); } scriptRequest.send(document.getElementById("tbxQuery").value); } function onCompletedTextFormate(statusCode, responseData, isTimeOut, responseXML) { if (!isTimeOut) { if ((statusCode < 200) || (statusCode >= 300)) { alert("Error occurred!"); } else { alert("Call back succeeded!"); document.getElementById("taeResponse").value = responseData; } } else { alert("Time out."); } } function onCompletedXMLFormate(statusCode, responseData, isTimeOut, responseXML) { if (!isTimeOut) { if ((statusCode < 200) || (statusCode >= 300)) { alert("Error occurred!"); } else { alert("Call back succeeded!"); document.getElementById("taeResponse").value = ""; for(var i=0; i<responseXML.firstChild.childNodes.length; i++) { document.getElementById("taeResponse").value += (responseXML.firstChild.childNodes[i].nodeName + " " + responseXML.firstChild.childNodes[i].childNodes[0].nodeValue +"\n"); } } } else { alert("Time out."); } } </script> Request Url: <input id="tbxUrl" type="text" style="width: 350px" value="SelfTestHandler.ashx"/><br /> Body: <input id="tbxBody" type="text" /> Query: <input id="tbxQuery" type="text" /> <input id="btnQueryText" type="button" value="QueryText" onclick="sendRequest(queryText);" /> <input id="btnQueryXml" type="button" value="QueryXml" onclick="sendRequest(queryXml);" /><br /> Response: <br />
<!-- <textarea id="taeResponse" style="width: 450px; height: 240px"></textarea> -->
</body> </html>
|
SelfTestHandler.ashx
<%@ WebHandler Language="C#" Class="SelfTestHandler" %>
using System; using System.Web;
public class SelfTestHandler : IHttpHandler { public void ProcessRequest (HttpContext context) { context.Response.ContentType = "text/html";
if (context.Request.QueryString["header"] == "queryText") { context.Response.Write( BuildResponse(context.Request["uniqueKey"], StatusCode, GetResponseDataTextFormat(context), IsTimeOut) ); } else { context.Response.Write( BuildResponse(context.Request["uniqueKey"], StatusCode, GetResponseDataXMLFormat(context), IsTimeOut) ); } } private int StatusCode { get { return 200; } } private bool IsTimeOut { get { return false; } }
private string GetResponseDataTextFormat(HttpContext context) { //Return text return HttpUtility.UrlPathEncode( string.Concat( "Return from my domain or other domain.\r\n", "Return from my domain or other domain.---中文返回测试\r\n", "Return from my domain or other domain.---html dom <tr><td>adfdsf<td></tr>\r\n", "Request Header: ", context.Request.QueryString["header"], "\r\n", "Request Body: ", context.Request.QueryString["body"], "\r\n", "Request Query: ", context.Request.QueryString["query"], "\r\n")); }
private string GetResponseDataXMLFormat(HttpContext context) { //Return xml return HttpUtility.UrlPathEncode( string.Concat( "<ServerReturn>", "<TestA>Return from my domain or other domain.</TestA>", "<TestB>Return from my domain or other domain.---中文返回测试</TestB>", "<TestC><![CDATA[Return from my domain or other domain.---html dom <tr><td>adfdsf<td></tr>]]></TestC>", "<TestD>Request Header: ", context.Request.QueryString["header"], "</TestD>", "<TestE>Request Body: ", context.Request.QueryString["body"], "</TestE>", "<TestF>Request Query: ", context.Request.QueryString["query"], "</TestF>", "</ServerReturn>" )); } private string BuildResponse(string uniqueKey, int statusCode, string responseData, bool isTimeOut) { return string.Concat("if(window[", uniqueKey, "] && window[", uniqueKey, "].complete){window[", uniqueKey, "].complete(", statusCode, ",", "", responseData, ",", isTimeOut.ToString().ToLower(), ");}"); }
public bool IsReusable { get { return false; } } }
|
ScriptRequest.js
//XML DOM parse object. window.XMLDOM = function(markup) { if (!window.DOMParser) { //IE var progIDs = [Msxml2.DOMDocument.3.0, Msxml2.DOMDocument]; for (var i = 0; i < progIDs.length; i++) { try { var xmlDOM = new ActiveXObject(progIDs[i]); xmlDOM.async = false; xmlDOM.loadXML(markup); xmlDOM.setProperty(SelectionLanguage, XPath); return xmlDOM; } catch (ex) { } } return null; } else { try { var domParser = new window.DOMParser(); return domParser.parseFromString(markup, text/xml); } catch (ex) { return null; } } return null; }
//Function delegate generator. Function.prototype.createDelegate = function(instance, method) { return function() { return method.apply(instance, arguments); } }
//ScriptRequestSender garbage collector class SenderGarbageCollector = function(collectPeriod) { this._garbageQueue = new Array(); this._collectPeriod = 5 * 60 * 1000; if(collectPeriod) { this._collectPeriod = collectPeriod; } window.setInterval(Function.createDelegate(this, this.clean), this._collectPeriod); }
SenderGarbageCollector.prototype = { add_Queue : function(item) { this._garbageQueue.push(item); }, clean : function() { for(var i=0; i<this._garbageQueue.length; i++) { var item = this._garbageQueue.shift() window[item] = null; } } }
//ScriptRequestSender global object. window.GlobalSenderGarbageCollector = new SenderGarbageCollector();
//ScriptRequestSender Class ScriptRequestSender = function() { this._timeOut = false; this._responseData = null; this._statusCode = 0; this._header = null; this._userContext = null; this._body = null; this._url = null; this._completeCallback = new Array(); this._uniqueKey = null; this._timer = null; this._scriptNode = null; }
ScriptRequestSender.prototype = { get_timeOut : function() { return this._timeOut; }, set_timeOut : function(timeOut) { this._timeOut = timeOut; }, get_responseData : function() { return this._responseData; }, get_statusCode : function() { return this._statusCode; }, get_userContext : function() { return this._userContext; }, set_userContext : function(userContext) { this._userContext = userContext; }, get_header : function() { return this._header; }, get_body : function() { return this._body; }, set_body : function(body) { this._body = body; }, get_url : function() { return this._url; }, set_url : function(url) { this._url = url; }, set_header : function(header) { this._header = header; }, get_xml : function() { return new XMLDOM(this.get_responseData()); }, add_completeCallback : function(onComplete) { this._completeCallback.push(onComplete); }, send : function(queryString) { var requestInfo = this.get_url(); var isSetQueryMark = false; if(this._header) { isSetQueryMark = true; requestInfo +=("?" + "header=" + encodeURIComponent(this._header)); } if(this._body) { if(!isSetQueryMark) { isSetQueryMark = true; requestInfo += "?"; } else { requestInfo += "&"; } requestInfo += ("body=" + encodeURIComponent(this._body)); } if(queryString) { if(!isSetQueryMark) { isSetQueryMark = true; requestInfo += "?"; } else { requestInfo += "&"; } requestInfo += ("query=" + encodeURIComponent(queryString)); } if(!isSetQueryMark) { isSetQueryMark = true; requestInfo += "?"; } else { requestInfo += "&"; }
this._uniqueKey = this._generateUniqueKey(); requestInfo += ("uniqueKey=" + encodeURIComponent(this._uniqueKey)); this._scriptNode = document.createElement("script"); this._scriptNode.type = "text/javascript"; this._scriptNode.language = "javascript"; this._scriptNode.src = requestInfo; //Persistent myself. window[this._uniqueKey.toString()] = this; window.GlobalSenderGarbageCollector.add_Queue(this._uniqueKey.toString());
document.getElementsByTagName("head")[0].appendChild(this._scriptNode); if(this._timeOut>0) { this._timer = window.setTimeout(Function.createDelegate(this, this._onTimeout), this._timeOut); } }, _generateUniqueKey : function() { return Math.random().toString(); }, _onTimeout : function() { this.complete(503, null, true); }, complete : function(statusCode, body, isTimeOut) { this._statusCode = statusCode; this._responseData = decodeURIComponent(body); this._timeOut = isTimeOut; if (this._timer) { window.clearTimeout(this._timer); } document.getElementsByTagName("head")[0].removeChild(this._scriptNode); for(var i=0; i<this._completeCallback.length; i++) { this._completeCallback[i](statusCode, this.get_responseData(), isTimeOut, this.get_xml()) } window[this._uniqueKey.toString()] = null; } }
|
其中的ScriptRequestSender 是发出请求的核心。由于在请求过程中支持并发请求。可能出现静态引用的Javascript对象,需要SenderGarbageCollector 来协助回收垃圾。
File: 本文实例代码
|
|
|
|
|
|