《ArcGIS API for JavaScript 开发教程》也告一段落,虽然很多还不是很清楚,但是心得还是有一点,我愿意将我的心得跟大家分享,喜欢和大家交流。
一个比较执着的技术男,一旦遇到了问题,他想的首先是解决这个问题,然后想的可能是为什么会遇到这样的问题,其次可能是解决此类问题能有几种方法,如果将这些都能理清,能说出个所以然来,我想这个技术男体会肯定颇深,我喜欢看这样的博文,尤其是一些关于什么什么的探究,或者运行机制,以至于本质,更厉害的博主直接就将一些底层的代码给剖出来,对于这样的博文或者博主,我只能望其项背,望洋兴叹。我也喜欢写类似的博文,因为这些是自己体会所得,但是资质平庸,我只能泛泛而谈,算不上什么上乘之作。
话题要从跨域开始,我在写《ArcGIS API for JavaScript 开发教程》的时候,当向服务器发送XmlHttpRequest请求的时候,经常遇到这个错误,虽然我成不了什么能将问题剖根到底,或者一针见血的指出其缘由,但是我喜欢将这些记录下来,只希望在不断的碰壁中得到正确的解决之道。之所以有这个问题,原因很简单,浏览器只允许请求当前源(域名、协议、端口)的资源,当浏览器发现两个请求不在同一个域中的时候,就报错。浏览器这么规定,那我们也只能接受,还是那句话,改变不了的那就默默接受吧!
浏览器没有错,因为浏览器出于安全考虑,是不允许JavaScript代码进行跨域操作。比如,liuyu.com站点中的Ajax只能访问liuyu.com站点下的资源,而不能跨域访问esrichina.com站点中的资源,这就是Ajax跨域问题。既然知道问题了,那么我们想的就是绕过跨域。
代理是一个解决方法,代理也是我用的最多的。
代理实际上起到了一个请求中转的作用,当A机器向C服务器请求的时候,A机器可以由B机器转发请求,然后A机器从代获取代理服务器B和C服务器的结果.代理也是一个Web服务器上运行的。常常使用动态网页(ASP.NET,PHP,JSP等)作为代理页面来作为这种中转请求的。上面这个关系应该一清二楚,毋庸置疑,代理在《ArcGIS API for JavaScript 开发教程》可以看到,这里就不多说了。
为什么在开发的时候可以直接使用Esri官网的在线的API?
页面在提交请求时,浏览器会进行Form身份验证(Form即为表单,每个页面都有一个Form标签),当向不同域获取数据时,浏览器认为这是不安全的,所以拒绝访问。 而标签script中的src请求远程服务数据时是不需要经过Form表单身份验证的,因为该标签并不包含在Form中,所以可以用此方法来请求不同域的数据。
测试,建立一个新的js文件内容如下:
function Add (a, b) { return a + b; } alert(Add(1,2)); <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>跨域测试</title> <script src="CrossTest.js" type="text/javascript"></script> <!-- <script src="http://localhost/CrossTest.js" type="text/javascript"></script>--> </head> <body> </body> </html>
我们将这个JS文件部署在服务器上,这样就跟调用Esri的在线API类似。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>跨域测试</title> <script src="CrossTest.js" type="text/javascript"></script> </head> <body> </body> </html> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>跨域测试</title> <script src="http://localhost/CrossTest.js" type="text/javascript"></script> </head> <body> </body> </html>
可以看到,这两个都可以得到正确的结果.说明了script标签不受跨域影响,基于这个,我们也可以创建script标签,动态的来进行处理,如下:
function ScriptTag(src){ var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } window.onload = function(){ ScriptTag("http://localhost/CrossTest.js"); }
http://www.cnblogs.com/chopper/archive/2012/03/24/2403945.html
JSONP,这个在使用esri.request的时候也常见。
JSONP是JSON with Padding的略称。它是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)
JSONP通过一个回调函数来实现跨域的,当构建http请求参数的时候,如下:JSONPTest.ashx?callback=callback&a=3&b=4,callback是在客户端的一个函数,当服务器处理结束后,将有客户端这个函数进行回调处理,我们可以看下,esri.request这个请求的结构。
esri.request的结构大体如下:
esri.request({
url:“yoururl”,
content:params,
callbackParamName:"callback",
load:function (result) {
//省略
},
error:function (error) {
//省略
});
}
这个的意思不言而喻,当执行成功的话去处理load对象的函数,失败的话去处理error对应的函数。
我们可以自己去模拟这个过程,下面是服务器端代码:
public class JSONPTest : IHttpHandler { public void ProcessRequest(HttpContext context) { //获取回调函数名 string callback = context.Request.QueryString["callback"]; JavaScriptSerializer js = new JavaScriptSerializer(); string a= context.Request.QueryString["a"]; string b = context.Request.QueryString["b"]; //json数据 StringBuilder Str= new StringBuilder(); context.Response.ContentType = "application/json"; js.Serialize(new { message = callback, result = Add(Convert.ToDouble(a), Convert.ToDouble(b)) }, Str); context.Response.Write(callback+"("+Str+")"); } public double Add(double a, double b) { return a + b; } public bool IsReusable { get { return false; } } }
客户端的代码,这里大家可以得到正确的值。
function callback(data) {
alert(data.result);
}
模拟是为了更好的了解,只在说明问题,当然你可能有更好的方法,现在很多框架都提供了JSONP这种方式,我们就没必要自己去折腾了。