There can be no Triumph without Loss,No Victory without Suffering,No Freedom without Sacrifice.
All you have to decide is what to do with the time that is given to you.
Get busy Living, or Get busy Dying?
  首页 | 留言给我 | 订阅 Rss | CLI | 黄白之恋 Posts:158   Hits: 4990063    Comments: 172    
 日历归档
<<  <  2024 - 03  >  >>
SuMoTuWeThFrSa
     12
3456789
10111213141516
17181920212223
24252627282930
31
 About Me
 Name: ZhangSichu
 Sex: Male
 Age: 32
 Email: ZhangSichu@gmail.com
 MSN: ZhangSichu@hotmail.com
 Home: ZhangSichu.com
 WeiBo: weibo.com/zhangsichu
 个人推荐
 分类归档
  ·C++/C(5)  RSS
  ·软件工程(1)  RSS
  ·杂事/随感(26)  RSS
  ·.Net/Java(30)  RSS
  ·面向对象程序设计(5)  RSS
  ·汇编/破解(0)  RSS
  ·平面设计(3)  RSS
  ·SQL(5)  RSS
  ·COM/COM+(2)  RSS
  ·Web开发(81)  RSS
 My Friends
Back Forward Refresh Home 2024年3月19日 星期二 RSS CLI Mine Sweeper. In Javascript.

  使用Script Request解决跨域请求问题
字体大小 [ ]

常用的Ajax框架,一般内部都封装了 XMLHttpRequest或者一个隐藏的Iframe来进行异步请求。XMLHttpRequest或者隐藏的Iframe是整个框架发出异步请求的引擎。但是由于浏览器对XMLHttpRequest和Iframe的安全限制,使得XMLHttpRequest只能请求本域内的资源。如果请求外域的资源,浏览器就会弹出提示或者错误。

IE中的提示:

Click to Open in New Window

Click to Open in New Window

FireFox中的错误:

Click to Open in New Window

使用隐藏的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中的错误:

Click to Open in New Window

FireFox 会直接终止Javascript执行。没有错误提示。

在一些场景下很需要请求外域的资源,来绘制自己页面的Dom。例如请求google的Service取得搜索相关的信息。请求天气网站的Service取得天气情况。通过这些信息绘制自己页面的区域。

如何解决跨域请求成了关键问题。常规的做法是使用自己本域下的一个链接地址作为Proxy。先把请求发会给自己的服务器,自己的服务器再从服务器端请求指定的站点,服务器端得到返回后再返回给用户的浏览器。通过服务器做Proxy解决跨域请求的问题。

在Html的Dom元素中<script /> 标签没有跨域限制。可以使用script 元素进行跨域请求。它从外域返回的Javascript 可以自由的操作本页面中的Dom元素或者javascript对象。

Click to Open in New Window

测试Demo:
1. 修改自己的 host 添加了127.0.0.1 www.niceclient.com 让www.niceclient.com解析到自己。
host - %windir%\system32\drivers\etc

Click to Open in New Window

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: 本文实例代码
  Posted @ 11/3/2007 7:35:37 PM | Hits (82945) | Comment (0

  Post Comment
标题 *
作者 *
密码 记住我
评论 *
    


Stable in Firefox 1.5 2.0Stable in IE6 IE7Stable in MozillaStable in Netscape
ZhangSichu.com V0.1.7507
Powered By ZhangSichu
Copyright © ZhangSichu
Download ZhangSichu.com source code. Download source code