2017.02.09 跨域
来源:互联网 发布:淘宝传感器 编辑:程序博客网 时间:2024/05/03 20:14
1、什么是跨域及产生原因
1.1 什么是跨域
跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com 页面去请求 www.google.com 的资源。
跨域的严格一点的定义是:协议(http&https)、端口(:80&:81)、域名(baidu&google)、二级域名(news&sports)不相同,都为跨域。
受到跨域的限制,浏览器不能用ajax获取不同源的数据,也不能在同一个页面但是处于不同域的框架之间的进行数据传递。
1.2 为什么浏览器要限制跨域访问
原因就是安全问题:如果一个网页可以随意地访问另外一个网站的资源,那么就有可能在客户完全不知情的情况下出现安全问题。比如下面的操作就有安全问题:
用户访问 www.mybank.com,登陆并进行网银操作,这时cookie啥的都生成并存放在浏览器。用户突然想起件事,并迷迷糊糊地访问了一个邪恶的网站 www.xiee.com ,这时该网站就可以在它的页面中,拿到银行的cookie,比如用户名,登陆token等,然后发起对www.mybank.com的操作。如果这时浏览器不予限制,并且银行也没有做响应的安全处理的话,那么用户的信息有可能就这么泄露了。
1.3 为什么要跨域
有时公司内部有多个不同的子域,比如一个是 location.company.com, 而应用是放在 app.company.com, 这时想从 app.company.com 去访问 location.company.com 的资源就属于跨域。
2、跨域的方法
2.1 document.domain+iframe方法
2.1.1 适用范围
适用于主域相同,子域不同的情况,并且需要对跨域的两个网页的js脚本都进行修改。例如在http://www.a.com/a.html和http://script.a.com/b.html之间的跨域就可以使用这种方法。
主域名是不带www的域名,比如 a.com, 主域名带前缀的通常是二级域名或者多级域名,比如 www.a.com。
2.1.2 如何实现
首先在a.html中创建一个iframe,将b.html通过iframe添加到a.html页面下,然后在 www.a.com/a.html 和 http://script.a.com/b.html 中都增加js脚本,脚本中加入代码:
document.domain="a.com"
这样就将两个原本跨域的网页的域统一了,此时就和平时同一个域镶嵌iframe一样,通过iframe的contentDocument中就可以实现数据交互。
页面默认的domain是window.loaction.hostname,document.domain只能设置成自身或更高一级的父域,比如 a.b.example.com 中的某个页面的document.domain可以设置为 a.b.example.com、 b.example.com、 example.com, 但是不能是设置 为c.a.b.example.com 或 baidu.com。
document.domain只能设置因此为保证两个主域相同,子域不同的页面跨域,二者的document.domain只能设置为二者的共同的主域,即a.com
iframe元素就是文档中的文档,拥有自己的事件,拥有自己的窗口对象(contentWindow)。浏览器会在打开一个 HTML 文档时创建一个对应的 window 对象。如果一个文档定义了一个或多个框架(即包含一个或多个frame或iframe 标签),浏览器就会为原始文档创建一个window对象,再为每个框架创建额外的window对象。这些额外的对象是原始窗口的子窗口,可能被原始窗口中发生的事件所影响。contentWindow属性是指指定的frame或者iframe所在的window对象,contentWindow兼容各个浏览器,可取得子窗口的 window 对象[2]。
2.1.3 限制
1 要实现跨域,a、b两个页面都必须由你自己来开发,对不受控制的对方进行跨域时无能为力;
2 依赖iframe,iframe的缺点也是一对,现在的大部分网站避免使用iframe。
历史上,iframe 常被用于复用部分界面,但是多数情况下并不合适。现在,应该使用 iframe 的例子如:
1. 沙箱隔离。
2. 引用第三方内容。
3. 独立的带有交互的内容,比如幻灯片。
4. 需要保持独立焦点和历史管理的子窗口,如复杂的Web应用。缺点也很明显:大量使用,打开一个网页加载过多iframe体验很不友好而且影响网页加载速度,对爬虫不够友好。[3]
2.1.4 代码示例
www.a.com上的a.html增加脚本:
<script type="text/javascript"> document.domain="a.com"; var ifr=document.createElement("iframe"); ifr.src="ttp://script.a.com/b.html"; ifr.style.display="no`ne"; document.body.appendChild(irf); irf.onload=function(){ var doc=irf.contentDocument|| ifr.contentWindow.document; //在这里操作b.html的contentWindow }</script>
script.a.com上的b.html增加脚本:
<script type="text/javascript"> document.domain="a.com";</script>
2.2 window.name方法
2.2.1 适用范围
适用于控制跨域双方代码的情况,也就是说需要对方的支持。
2.2.2 如何实现
将json格式的数据写入到window.name中,通过共享一个窗口的两个页面的共同的window.name实现跨域的数据传递
window.name属性在一个窗口(window)生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写权限。
window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而重置。window.name只能是字符串,最大允许2M左右数据,可以传递json格式的数据。
a.html和b.html可以通过window.location实现同一个窗口下的跳转,这样a就可以取得b页面中window.name中写入的数据。
如果想要不同源的a页面在不由b页面跳转而来的情况下获得数据,就不能直接使用window.location实现,需要利用一个隐藏的iframe来充当跳板,由iframe获取b页面的数据(需要将iframe的src设置为b页面的地址),然后需要将iframe的src再次设置为与a页面同源,a页面再去获取iframe的数据。
2.2.3 限制
1 同样的,a、b两个页面都必须由你自己来开发,因为需要将数据写入到window.name中,因此对不受控制的对方进行跨域时无能为力;
2 window.name只能是字符串,最大允许2M左右数据,还是有一定限制的。
2.2.4 代码示例
第一种情况就是发生跳转的情况。
如页面b.html的代码如下:
<script>window.name="jay111";setTimeout(function(){ window.location="../test10/test10.html"},3000)</script>
页面a.htm的代码如下:
<script> function doSomething(jsonDate){ alert(window.name) } doSomething()</script>
当b页面跳转a页面后,a会取得在b页面中写入window.name的值jay111。
第二种情况就是不发生跳转,使用iframe作为跳板的情况。
b页面代码如下:
<script> window.name="jay111";</script>
a页面的代码如下:
<body><iframe id="test" src="b.html"></iframe><script> var iframe=document.getElementById("test"); iframe.onload=function() { iframe.src = "c.html";//这里c.html为任意的页面,只需要与a.html同源既可,设置成about:blank也可以 iframe.onload = function () { var data = iframe.contentWindow.name;//获取a.html中的window.name数据 alert(data); } }</script></body>
注意,更改iframe的src需要在iframe载入后执行,所以添加到onload事件里。
2.3 postMessage方法
2.3.1 适用范围
页面和其打开的新窗口的数据传递、多窗口之间消息传递、页面与嵌套的iframe消息传递都可以使用postMessage方法跨域或同源传递数据。[4]
postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。[5]
2.3.2 如何实现
window. postMessage(message,targetOrigin)方法是HTML5引入的新特性,用来向其他的window对象发送消息,无论其他的window对象是否跨域。语法如下:
window.postMessage(message,targetOrigin);
message是要发送的string数据,HTML5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化。
targetOrigin用来限制otherWindow对象所在的域,字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为”*”,这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/”。
接受消息的window对象otherWindow是通过监听自身的message事件获取传过来的消息,消息内容存储在message事件对象的data属性中。
2.3.3 限制
对不受控制的对方进行跨域时无能为力;
2.3.4 代码示例
a.com上的a.html的代码:
<iframe id="ifr" src="b.com/b.html"></iframe><!--对接受信息页面的引用--><script type="text/javascript"> window.onload=function(){ var ifr=document.getElementById("ifr").contentWindow; //获取框架的window对象 var targetOrigin="http://b.com"//对目标的限定 ifr.postMessage("i love you",targetOrigin); }</script>
b.com上的b.html的代码:
<script type="text/javascript"> window.addEventListener("message",function(event){//注册message事件用来接收消息 if(event.origin==="http://a.com"){//通过origin判断消息来源地址 alert(event.data);//通过data属性获取"i love you" alert(event.source);//对a.com/a.html中window对象的引用 alert(event.origin);//发送消息窗口的源(协议+主机+端口号) } },false)</script>
上面的集中方法都主要侧重于前端通讯。
2.4 动态创建script
2.4.1 适用范围
两个域开发者都需要控制,被访问的域需要返回一段JS代码。
2.4.2 如何实现
浏览器可以在页面中引用其他域的JS文件,并且执行引入的JS的文件中的function,所以可以通过创建script的方法实现跨域通信。
原理就是在本域内页面a的
2.4.3 限制
两个域开发者都需要控制。
2.4.4 代码示例
详细代码以前曾经总结过,详见《2016.12.05 JSON和JSONP》(http://note.youdao.com/noteshare?id=89adc08bbc61d9735f324dd68da83425&sub=7C193C83D0454923B7D41ABE9961C69D)
2.5 JSONP方法
2.5.1 适用范围
使用返回与上一个方法相同,开发者都需要控制两个域。
2.5.2 如何实现
利用
2.5.3 限制
1 服务器端需要部署相应的代码,响应客户端的请求。
2 只支持GET,不能很好的支持POST。
2.5.4 代码示例
本地服务器上同时运行了2个project,端口分别是8080和8081,在8081的客户端上面请求8080端口的数据,端口不同也是跨域。[6]
//getJOSN方法$.getJSON("http://localhost:8080/msg/front/jcj/t1.jsp?callback=?",function(result) {});//ajax方法$.ajax({type: "get", url: "http://localhost:8080/msg/front/jcj/t1.jsp",//也可以直接将callback写在url中//url: "http://localhost:8080/msg/front/jcj/t1.jsp?callback=m1", dataType: "jsonp", jsonpCallback: "m1", success:function(data){ //处理返回的数据 }});function m1(data) { alert(data);}
服务器端需要部署相应的代码,响应客户端的请求。服务器端部署的代码可以是JAVA代码:
String str = "{[\"name1\": \"json\",\"name2\": \"json\",\"name3\": \"json\"]}";ObjectMapper mapper = new ObjectMapper();String json = mapper.writeValueAsString(str);json = "m1(" + json + ")";response.getWriter().print(json);
也可以是PHP代码:
<?php$callback=$_GET['callback'];//得到回调函数名$data=array('a','b','c');//要返回的数据echo $callback.'('.json_encode($data).')';//echo输出?>
说明:第一种方式的callback=?回调函数标志着Ajax请求是以jsonp的方式发送请求,客户端请求会自动在问号处增加一个方法名,方法名是一jquery开头的一串数字,而第二种方式的是参数dataType=”jsonp”来说明是以jsonp的方式发送请求,所以可以直接在callback后面直接指定方法名callback=m1,而不需要jsonpCallback: “m1”这一样代码。
AngularJS的http也提供了对JSONP的支持,可以直接调用jsonp进行跨域访问;
知道了一个免费的API的实例(http://www.opencai.net/apifree/),
可以使用下面的代码获取响应的数据:
$.getJSON("http://f.apiplus.cn/ssq-12.json?callback=?", function(result){ alert(result.data[1].opencode) } );
结果:
从这个里也可以开出,服务器端要布置响应的代码,并且把API放出来给使用者,最好配有相应的接口说明文档
2.6 XHR2(XMLHTTPREQUEST LEVEL2)方法
2.6.1 适用范围
在服务端进行处理,客户端支持H5
2.6.2 如何实现
HTML5中提供的XMLHTTPREQUEST Level2实现了跨域访问,只需要在服务端填上响应头[7]:
header(“Access-Control-Allow-Origin:*”);/*星号表示接受来自所有域的访问*/header(“Access-Control-Allow-Methods:GET,POST”);
2.6.3 限制
1 服务器端需要部署相应的代码,响应客户端的请求。
2 IE10一下不支持这种方式。
2.7 nginx反向代理跨域
上面的所有的方法都有一个共同的限制,那就是必须在对方的网页(或者服务器)上进行相应的部署,不论是JS代码还是服务器端代码。
但是有一种最常遇到的情形,就是想要获取的数据的网站是不受自己控制的,开发者只能控制一个域。
这时候只能依靠nginx进行反向代理跨域。
3 nginx
3.1 关于nginx
nginx(音同engine x)是一个强大的轻量级的高性能网页服务器、反向代理服务器和电子邮件代理服务器。作为负载均衡服务器,nginx可以在内部直接支持Rails和PHP程序对外进行服务,也可以支持作为HTTP代理服务器对外进行服务。
3.2 nginx的正向代理
正向代理的工作原理就像一个跳板,简单的说,我是一个用户,我访问不了某网站,但是我能访问一个代理服务器这个代理服务器呢,他能访问那个我不能访问的网站。
于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我。从网站的角度,只在代理服务器来取内容的时候有一次记录。有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。
结论就是正向代理是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。
3.3 nginx的反向代理
3.3.1 什么是反向代理
所谓反向代理(Reverse Proxy)方式是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器[8]。
从上图可以看出,反向代理服务器位于网站机房,代理网站web服务器接受Http请求,对请求进行转发。
例用户访问 http://ooxx.me/readme, 但ooxx.me上并不存在readme页面,他是偷偷从另外一台服务器上取回来,然后作为自己的内容吐给用户,但用户并不知情。这里所提到的ooxx.me这个域名对应的服务器就设置了反向代理功能。
结论就是反向代理正好相反,对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端,就像这些内容原本就是它自己的一样。
3.3.2 反向代理的作用
1 保护网站安全:任何来自Internet的请求都必须先经过代理服务器;
2 通过配置缓存功能加速web请求:可以缓存真实Web服务器上的某些静态资源,减轻真实Web服务器的负载压力;
3 实现负载均衡:充当负载均衡服务器均衡的奋发请求,平衡集群中各个服务器的负载压力;
3.4 利用nginx的反向代理实现跨域
利用nginx反向代理实现跨域,不需要目标服务器配合,但需要搭建中转nginx服务器,用于转发请求。
3.4.1 实现原理
黑色的过程是nginx作为反向代理服务器,隐藏了真实地址的过程,紫色的过程就是用nginx实现跨域的过程。
3.4.2 具体操作
首先找到nginx.conf中的下面这部分内容:
server { listen 80; server_name localhost; location / { root ../Project; index index.html index.htm; }
其中server代表启动的一个服务,location是一个定位规则,是nginx用来跨域的入口。
location /的意思是所有以/开头的地址,实际上是所有请求,后面的地址可以是绝对地址,也可以是相对地址(相对nginx.exe的地址);root的意思是去请求相对nginx.exe上一层中的Project文件夹里的文件,index是去指定首页。
在location /{}中添加一下代码:
add_header’ Access-Control-Allow-Origin’’*’;add_header’ Access-Control-Allow-Credentials’’true’;add_header’ Access-Control-Allow-Methods’’GET,POST,OPTIONS’;rewrite regex replacement [flag]proxy_pass http://ip:port/;
其中:
第一条指令:授权从other.subdomain.co的请求;是W3C标准里用来检查该跨域请求是否可以被通过;
第二条指令:当该标志为true时,响应于该请求可以被暴露;
第三条指令:制定请求的方式,可以使GET/POST等;
第四条指令:对域名(根路径)后面除去传递的参数外的字符串的url进行重写及重定向;
URL 是 URI 的子集。任何东西,只要能够唯一地标识出来,都可以说这个标识是 URI 。如果这个标识是一个可获取到上述对象的路径,那么同时它也可以是一个 URL ;但如果这个标识不提供获取到对象的路径,那么它就必然不是 URL[9]。
第五条指令:实际要访问的请求地址;
前三条时用来指定自己的服务器是否可以被跨域访问的指令,是可以没有的。
第四条利用nginx提供的变量,结合正则表示和标志位来实现比较复杂的重定向,简单的情况也是可以没有的。
3.4.2.1 location+proxy_pass
适用于将location直接转发至proxy_pass地址的情况,这种情况一般比较简单,例子
:
<script type="text/javascript"> var button=document.getElementById("button"); $(button).click(function(){ $.get("students",function(data){ alert(data.data[0].id) },"json"); $.getJSON("students",function(data){ alert(data.data[0].id) }); $.ajax({ url:"students", type:"GET", dataType:"json", success:function(data){ alert(data.data[0].id)} }) })</script>
上面的三种方法都可以实现跨域,要注意的是使用get方法获取json数据时一定要声明第四个参数dataType为JSON,否则总是无法获得正确的对象。
跨域的部分,url地址是一个相对地址”students”,转化为绝对地址就是:
http://localhost/Project2/task5/students/
而在实际本机的地址中,并没有这样的资源,所以需要利用nginx进行转发处理。
location ^~/Project2/task5/students/{ proxy_pass http://115.29.203.53:10013/students/;}
配置完成后重启nginx服务
nginx.exe -s reload
nginx的启动和停止需要在命令行中定位到nginx.exe所在的文件夹,然后:
启动:start nginx
停止:nginx -s stop或nginx -s quit
注:stop是快速停止nginx,可能并不保存相关信息;quit是完整有序的停止nginx,并保存相关信息。
重新载入Nginx:nginx -s reload当配置信息修改,需要重新载入这些配置时使用此命令。
重新打开日志文件:nginx -s reopen
查看Nginx版本:nginx -v[11]
这个是比较蠢的做法,直接将全部的url替换为最终的真实url,这样就将地址转发出去了,实现了跨域。
3.4.2.2 location+proxy_pass+rewrite
适用于直接将location转发至proxy_pass不能满足需要,需要对虚拟根路径及后面的路径使用rewrite进行改写的情况,例子:
我们主机的地址是 www.a.com/html/index.html, 想请求 www.b.com/api/msg?method=1¶=2, ajax请求如下:
$.ajax({ type:"get", url:"www.b.com/api/msg?method=1¶=2", success:function(res){ alert("success") }})
这样必然因为跨域问题发生错误,无法获得b网站的数据。将ajax请求更改为:
$.ajax({ type:"get", url:"api/msg?method=1¶=2", //真实地址是www.c.com/proxy/html/api/msg?method=1¶=2,www.c.com是nginx主机地址 success:function(res){ alert("success") }})
这是url就是相对于a网站的本地的相对地址了,但是在本地并不存在相应的数据,所以需要将这个地址通过nginx转发出去。
在刚才的路径中匹配到这个请求,在location下面再添加一个location:
location ^~/proxy/html/{//#匹配任何以/proxy/html/开头的地址,匹配符合以后不继续往下搜索 rewrite ^/proxy/html/(.*)$ /$1 break; proxy_pass http://www.b.com/}
location ^~/proxy/html/用于拦截请求,是一个匹配规则,匹配任何以/proxy/html/开头的地址,这里匹配到的就是:
www.c.com/proxy/html/api/msg?method=1¶=2
rewrite ^/proxy/html/(.*)
rewrite后面是一个正则表达式,表示匹配以/proxy/html/开头的任何字符至结尾,并且将proxy/html/后面的任意字符存到第一个捕获组$1之中,break表示匹配一个后停止。
重写的结果是:/api/msg
proxy_pass http://www.b.com/ 用来把请求代理到其他主机,即 www.c.com 代理到 www.b.com, 请求路径最终变为
http://www.b.com/api/msg? method=1¶=2。
配置完成后重启nginx服务
这样就可以更改3.4.2.1中的做法,js文件不变,nginx的location的配置如下:
location ^~/Project2/task5/students/{ rewrite ^/Project2/task5/(.*)$ /$1 break; proxy_pass http://115.29.203.53:10013/; }
同样可以实现跨域
3.4.3 关于location[10]
location是主机访问的地址,在nginx服务器做一个代理,转发到location中配置的地址。
location =/{ #精确匹配,主机名后面不能带任何字符串 [configuration]}location /{ #因为所有的地址都以/开头,所以这条规则将匹配到所有请求,通用匹配 #但是正则和最长字符串会优先匹配 [configuration]}location ~/document/abc{ #匹配任何以/document/开头的地址,匹配符合以后,还要继续往下搜索 #只有后面的正则表达式没有匹配到时,这一条才会被采用 [configuration]}location ^~/document/{ #匹配任何以/document/开头的地址,匹配符合以后不继续往下搜索 [configuration]}
3.4.4 关于rewrite
rewrite只能对域名(根地址)后边的除去传递的参数外的字符串起作用,例如:
http://www.baidu.com/a/bb/ccc.php?id=1&uu=str
rewrite只会对a/bb/ccc.php重写。
语法:
rewrite regex replacement [flag]
执行顺序是首先执行location匹配,然后执行location中的rewirte指令。
3.4.5 关于proxy_pass
proxy_pass地址后面加上了/则相当于转发到绝对根路径,nginx不会对loaction中匹配的路径部分进行转发,例如[12]:
location ^~/proxy/html/{//#匹配任何以/proxy/html/开头的地址,匹配符合以后不继续往下搜索 proxy_pass http://www.b.com/}
如果请求的url为:http://localhost/proxy/html/test.json, 则转发后的地址是:http://www.b.com/test.json。
如果proxy_pass地址后面不加/,则nginx会对loaction中匹配的路径部分进行转发,例如:
location ^~/proxy/html/{//#匹配任何以/proxy/html/开头的地址,匹配符合以后不继续往下搜索 proxy_pass http://www.b.com}
如果请求的url为:http://localhost/proxy/html/test.json, 则转发后的地址是:http://www.b.com/proxy/html/test.json。
3.4.6 又一个例子[13]
$.ajax({ type:"get", url:"sohu/api/msg?method=1¶=2", //真实地址是www.c.com/proxy/html/api/msg?method=1¶=2,www.c.com是nginx主机地址 success:function(res){ alert("success") }})
location ^~/sohu{//#匹配任何以/proxy/html/开头的地址,匹配符合以后不继续往下搜索 rewrite ^.+sohu/?(.*)$ /$1 break; proxy_pass http://www.sohu.com/}
location定位到:sohu/api/msg?method=1¶=2
rewrite的结果:api/msg
proxy_pass后的结果:http://www.sohu.com/api/msg?method=1¶=2
3.4.7 最后一个例子
终于把这个总结写完了,从2016年写到了2017年,拖了很长时间,不过写完了自己也明白了,任务可以做了。
写这些东西,也是怕以后自己如果长时间不用后再用的时候会忘记,好可悲,不过自己写的东西符合自己的理解思路,捡起来会跨一点。
还是好可悲。
5、参考
- http://blog.csdn.net/notechsolution/article/details/50394391
- http://blog.csdn.net/dongzhiquan/article/details/585120
- https://www.zhihu.com/question/20653055
- http://www.qdfuns.com/notes/13989/cab4fcdd13851f75bb8af349d5596baf.html
- http://www.cnblogs.com/dolphinX/p/3464056.html
- http://www.cnblogs.com/dreamroute/p/3613563.html
- http://www.jb51.net/article/77470.htm
- http://blog.csdn.net/ywl570717586/article/details/51556912
- https://www.zhihu.com/question/19557151
- https://segmentfault.com/a/1190000002797606
- http://blog.csdn.net/ppby2002/article/details/38681345
- http://stevenlee87.blog.51cto.com/996623/1188295
- http://blog.csdn.net/shendl/article/details/48443299
- 2017.02.09 跨域
- [leetCode刷题笔记]2017.02.09
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- 跨域
- //跨域
- 安卓客户端上传图片到服务器
- jdk的安装与环境变量配置
- 笔记1
- Nginx使用小记
- HomePlug将实现智能、联网的舒适生活
- 2017.02.09 跨域
- Android中.9.png的制作
- 一个poll函数使用的例子
- Java developer interview questions: The hard part
- 用友uap nc65开发报错:当前用户没有可用的查询模板
- TensorFlow安装成功后导入IDE报错
- 【题解】Fathest Node in a Tree - 边表法
- 彻底弄懂JS的事件冒泡和事件捕获
- 数字求和