首页 » 网站推广 » getdataphp技巧_第69节 跨域请求Web前端开拓之JavaScript零点轨范员王唯

getdataphp技巧_第69节 跨域请求Web前端开拓之JavaScript零点轨范员王唯

访客 2024-12-03 0

扫一扫用手机浏览

文章目录 [+]

当代的所有的浏览器都遵守同源策略,所谓的源指的便是一个域名、一个做事器,同源是指两个脚本拥有相同的域名,不同源指的是不同的域名,同源策略指的是,一个源的脚本不能读取或操作其他源的http相应和cookie,也便是出于安全方面的考虑,页面中的JavaScript无法访问其他做事器上的数据,这便是所谓的“同源策略”。
同源是指协议、域名和端口都同等。

不同源限定的内容:

getdataphp技巧_第69节 跨域请求Web前端开拓之JavaScript零点轨范员王唯

Cookie、LocalStorge、IndexedDb等存储性内容;DOM节点;Ajax要求;

跨域要求时,不同域的做事器是返回了数据的,只不过浏览器拦截了相应数据;同时也解释了跨域并不能完备阻挡CSRF,由于要求毕竟是发出去了;

getdataphp技巧_第69节 跨域请求Web前端开拓之JavaScript零点轨范员王唯
(图片来自网络侵删)

CORS(Cross-Origin Response Sharing)跨域资源共享:

通过XHR实现Ajax通信的紧张限定,是跨域安全策略;默认情形下,只能访问同一个域中的资源,这种安全策略可以预防某些恶意行为,如:

var xhr = new XMLHttpRequest();xhr.onload = function(){console.log(xhr.responseText);}xhr.open("GET", "https://www.zeronetwork.cn/study/index.html");xhr.send(null);

其抛出了CORS policy非常;

XHR2规范了在通过HTTP相应中如何选择得当的CORS(Cross-Origin Response Sharing,跨域资源共享)去跨域访问资源;其定义了在必须访问跨源资源时,浏览器与做事器该当如何沟通;CORS的基本思想是,便是利用自定义的HTTP头部让浏览器与做事器进行沟通,从面决定要求或相应是否该当成功;

比如一个大略的利用GET或POST发送的要求,默认情形下它没有自定义的头,但一样平常会包括一个Origin要求头,个中包含要求页面的源信息(协议、域名和端口),以便做事器根据这个头部信息来决定是否给予相应,如Origin头部示例:

Origin: https://www.zeronetwork.cn

如果做事器认为这个要求可以接管,就在相应的Access-Control-Allow-Origin([əˈlaʊ])头中回发相同的源信息(如果是公共资源,可以回发””),例如:

Access-Control-Allow-Origin: https://www.zeronetwork.cn

如果没有这个相应头,或者有这个相应头但与要求Origin头信息不匹配,浏览器就会驳回要求;反之,浏览器会处理要求;

实现跨域:

IE和标准浏览器已经实现了各自的跨域办理方案;

标准浏览器对CORS的实现:

在标准浏览器中,客户端在利用Ajax跨域要求时,抛出非常,不能访问;如:

var xhr = new XMLHttpRequest();xhr.onreadystatechange = function(){if (xhr.readyState == 4 && xhr.status == 200) {console.log(xhr.responseText);}}xhr.open("GET","https://www.b.com/normal/example.json");xhr.send(null);

被要求的做事端须要设置Access-Control-Allow-Origin相应头,以便于浏览器识别它是否为可信源。

例如,在Apache做事器中,在做事器的配置中添加如下设置:

Header set Access-Control-Allow-Origin 'origin-list'

对付Nginx,设置此http头的命令是:

add_header 'Access-Control-Allow-Origin' 'origin-list'

或者利用.htaccess文件配置,如:

<IfModule mod_headers.c>Header set Access-Control-Allow-Origin ""</IfModule>order allow,denyallow from all

运用:

xhr.open("GET","https://www.b.com/cors/example.json");

单独为某个后端程序设置相应头,例如b.com/cors.php:

<?phpheader("Access-Control-Allow-Origin: ");echo "跨域访问b.com/cors.php";

无论同源要求还是跨源要求都利用相同的接口,因此对地本地资源,最好利用相对URL,在访问远程资源时再利用绝对URL;这样做能肃清歧义,避免涌现限定访问头部或本地cookie信息等问题。

IE对CORS的实现:

微软在IE8中引入了XDR(XDomainRequest)工具,其与XHR类似,其可直接用于发起安全的跨域要求,实现安全可靠的跨域通信;

var xdr = new XDomainRequest();console.log(xdr);

IE11和标准浏览器并不支持;

XDR工具的利用方法与XHR工具非常相似,两者拥有险些相同的属性和方法,也是调用open()方法,再调用send()方法;但与XHR工具的open()方法不同,XDR工具的open()方法只吸收两个参数:要求的类型和URL;如:

var xdr = new XDomainRequest();console.log(xdr);// xdr.open("GET", "http://www.c.com/nocors.php");xdr.open("GET", "example.php");xdr.onload = function(){console.log(xdr.responseText);}xdr.send();

此时,不管是跨域的还是同源的都不许可访问,抛出“在 Access-Control-Allow-Origin 标头中未找到源”;

XDR工具的安全机制中部分实现了CORS,后端也须要设置 Access-Control-Allow-Origin相应头,如c.com/cors.php:

<?phpheader("Access-Control-Allow-Origin: ");echo "设置了ACAO相应头";

要求端:

xdr.open("GET", "http://www.c.com/cors.php");

XDR工具属性和事宜:

要求返回后,会触发onload事宜,相应的数据也会保存在responseText属性中,相应的MIME类型保存在contentType属性中,如:

var xdr = new XDomainRequest();xdr.onload = function(){console.log(xdr.contentType); // application/jsonconsole.log(xdr.responseText);}xdr.open("post","http://www.c.com/cors/example.json");xdr.send(null);

例如再要求一个同源的contentType.php:

header("Access-Control-Allow-Origin: ");header("Content-Type: application/json");echo '{"username":"王唯","age":18,"sex":true}';

在吸收到相应后,只能访问相应的原始文本,不能确定相应的状态代码(也便是它没有status属性);而且,只要相应有效就会触发onload事宜,如果失落败就会触发error事宜,但除了缺点本身之外,没有其他信息可以确定要求是否成功,以是唯一能够确定的就只有要求未成功;

要检测缺点,如要指定error事宜处理程序,如:

xdr.onerror = function(){console.log("涌现缺点");}

由于导致XDR要求失落败的成分很多,因此,最好通过error事宜处理程序来捕获该事宜,否则,纵然要求失落败也不会有任何提示。

在要求返回前调用abort()方法可以终止要求,如:

xdr.abort();

与XHR工具一样,XDR也支持timout属性和ontimeout事宜,如:

xdr.timeout=1000;xdr.ontimeout = function(){console.log("要求超过1秒");};

onprogress事宜:

该当始终定义 xdr.onprogress 事宜,纵然它是一个空函数,否则 XDomainRequest 对付重复要求,可能不会触发 onload 事宜;

xdr.onprogress = function(event){console.log(event);};

XDR与XHR的不同之外:

1、必须利用 HTTP 或 HTTPS 协议访问目标 URL:由于XDR工具依赖于一个HTTP相应头来实现访问掌握,以是它哀求目标URL符合HTTP或HTTPS 协议,以便于XDR工具考验相应头;2、只支持GET和POST要求;3、不能设置自定义要求头信息,也不能访问相应头部信息;4、只支持text/plain作为要求头Content-Type的取值:在XDR中,不管是GET还是POST要求,Content-Type被限定成“text/plain”,以是做事端不会把要求主体数据解析成键值对,即不能从参数中获取到POST的数据,只能读取流数据,须要其自行解析,如:

var xdr = new XDomainRequest();xdr.open("POST", "xdrpost.php");xdr.onload = function(){console.log(xdr.responseText);};var param = "username=wangwei&age=18";xdr.send(param);

xdrpost.php:

<?phpheader("Access-Control-Allow-Origin: ");$content = file_get_contents("php://input");// echo $content; // username=王 唯&age=18$arr = explode("&", $content);foreach ($arr as $value) {$v = explode("=", $value);echo "key:$v[0], value:$v[1] \r\n";}

5、身份验证和cookie不能和要求一起发送,也不会随相应返回;6、要求的URL必须和被要求的URL采取相同的协议:两者的协议必须统一,要么是HTTP,要么是HTTPS;7、所有XDR要求都是异步实行的,不能用它来创建同步要求;

Preflighted Requests:

CORS通过一种叫做Prelighted Requests的透明做事器验证机制支持开拓职员利用自定义的头部、GET或POST之外的要求办法,以及不同类型的主体内容;在利用下列高等选项来发送要求时,就会向做事器发送一个Preflight要求,这种要求利用OPTIONS办法,发送下列头部:

Origin:与大略的要求相同;Access-Control-Request-Method:要求自身利用的方法;Access-Control-Request-Headers:(可选)自定义的头部信息,多个头部以逗号分隔;

以下是一个带有自定义头部customHeader,并利用POST方法发送的数据,如:

Origin: http://www.zeronetwork.cnAccess-Control-Request-Method: POSTAccess-Control-Request-Headers: customHeader

发送这个要求后,做事器端可以决定是否许可这种类型的要求,其通过在相应中发送如下头部与浏览器进行沟通:

Access-Control-Allow-Origin:与大略的要求相同;Access-Control-Allow-Methods:许可的方法,多个方法以逗号分隔;Access-Control-Allow-Headers:许可的头部,多个头部以逗号分隔;Access-Control-Max-Age:该当将这个Preflight要求缓存多永劫光(以秒表示)

例如,许可任何源、POST要求办法、自定义头customHeader以及要求的缓存韶光:

Access-Control-Allow-Origin: https://www.zeronetwork.cnAccess-Control-Allow-Methods: POSTAccess-Control-Allow-Headers: customHeaderAccess-Control-Max-Age: 1728000

如:

var xhr = new XMLHttpRequest();xhr.onload = function(){console.log(xhr.responseText);}xhr.open("OPTIONS","https://www.b.com/flighted.php",true);xhr.setRequestHeader("customHeader", "customValue");xhr.send(null);

后端flighted.php:

<?phpheader("Access-Control-Allow-Origin: ");header("Access-Control-Allow-Headers: ");header("Access-Control-Allow-Methods: ");header("Access-Control-Max-Age: 1728000");echo "有Headers、Methods头信息";

Preflight要求结束后,结果将按照相应中指定的韶光缓存起来;

带凭据的要求:

默认情形下,跨域要求不供应凭据(cookie、HTTP认证及客户端SSL证明等);通过将XHR工具的withCredentials属性设置为true,可以指定某个跨域要求该当发送凭据(授权信息);如:

xhr.withCredentials = true;xhr.send(null);

当利用带有凭据的要求时,不能把Access-Control-Allow-Origin设为,并且Access-Control-Allow-Origin只能设置一个域,不能是多个,否则会抛出非常;

后端c.com/credentials.php:

header("Access-Control-Allow-Origin: http://www.a.com");echo "c.com/example.php,已经设置了ACAO";

如果做事端接管带凭据的要求,必须设置Access-Control-Allow-Credentials: true相应头;

如后端c.com/ credentials.php:

header("Access-Control-Allow-Origin: http://www.a.com");header("Access-Control-Allow-Credentials: true");echo "设置了Origin,也设置了Credentials";echo json_encode($_COOKIE);

如果在同源下配置withCredentials,无论配置true还是false,效果都会相同,且会一贯供应凭据信息;其余,同时还可以发送自定义要求头,如后端credentials.php:

<?phpheader("Access-Control-Allow-Origin: http://www.a.com");header("Access-Control-Allow-Credentials: true");header("Access-Control-Allow-Headers: customHeader");echo "设置了Origin,也设置了Credentials";echo json_encode($_COOKIE);

做事端还可以在Preflight相应中发送这个HTTP头部,但不能把Access-Control-Allow-Headers设为;

跨浏览器的CORS:

纵然浏览器对CORS的支持程度并不一致,但所有浏览器都支持大略的要求(非Preflight和不带凭据的要求),因此有必要实现一个跨浏览器的方案:检测XHR是否支持CORS的最大略办法,便是检讨是否存在withCredentials属性,再结合检测XDomainRequest工具是否存在,就可以兼顾所有浏览器了,如:

function createCORSRequest(method, url, withCredentials){var xhr = new XMLHttpRequest();if ("withCredentials" in xhr) {xhr.open(method, url);xhr.withCredentials = withCredentials;}else if(typeof XDomainRequest != "undefined"){xhr = new XDomainRequest();xhr.open(method, url);}else{xhr = null;}return xhr;}var request = createCORSRequest("GET", "https://www.b.com/credentials.php", true);if(request){request.onload = function(){console.log(request.responseText);};request.send(null);}

示例:利用HEAD和CORS要求链接的详细信息,如:

var supportsCORS = (new XMLHttpRequest).withCredentials != undefined;var links = document.getElementsByTagName("a");for(var i=0; i<links.length; i++){var link = links[i];if(!link.href) continue;if(link.title) continue;if(link.host !== location.host || link.protocol != location.protocol){link.title = "站外链接";if(!supportsCORS) continue;}if(link.addEventListener)link.addEventListener("mouseover", mouseoverHandler, false);elselink.attachEvent("onmouseover", mouseoverHandler);}function mouseoverHandler(e){var link = e.target || e.srcElement;var url = link.href;var xhr = new XMLHttpRequest();xhr.open("HEAD", url);xhr.onreadystatechange = function(){if(xhr.readyState !== 4) return;if(xhr.status == 200){var type = xhr.getResponseHeader("Content-Type");var size = xhr.getResponseHeader("Content-Length");var date = xhr.getResponseHeader("Last-Modified");link.title = "类型:" + type + "\n" +"大小:" + size + "\n" +"韶光:" + date;}else{if(!link.title)link.title = "获取不到详细信息:\n" +xhr.status + " " + xhr.statusText;}};xhr.send(null);if(link.removeEventListener)link.removeEventListener("mouseover", mouseoverHandler, false);elselink.detachEvent("onmouseover", mouseoverHandler);}

HTML:

<a href="https://www.zeronetwork.cn/edu/">edu</a><a href="https://www.zeronetwork.cn/study/">study</a><a href="http://www.a.com/ demo.html">a.com</a><a>no href</a><a href="https://www.apple.com" title="baidu">apple.com</a><a href="https://cn.bing.com">bing</a>

其它跨域技能:

虽然CORS技能已经无处不在,但在CORS涌现之前,就已经存在一些跨域的技能了,虽然这些技能运用起来有些麻烦,但它们绝大部分不须要修正做事器端代码,以是直到现在这些技能仍旧被广泛利用;

后端代理办法:

这种办法可以办理所有跨域问题,也便是将本域的后端程序作为代理,每次对其它域的要求都转交给该代理程序,其通过仿照http要求去访问其它域,再将返回的结果返回给前端,这样做的好处是,无论访问的是文档、还是JS文件都可以实现跨域;例如,b.com/data.php相应JSON字符串:

<?php$json_str = '{"name":"wagwei","sex":true,"age":18}';echo $json_str;

a.com/getdata.php做事端获取b.com/data.php相应:

<?php// 创建cURL资源$ch = curl_init();// 设置URL和相应的选项curl_setopt($ch, CURLOPT_URL, "http://www.b.com/data.php");curl_setopt($ch, CURLOPT_HEADER, 0);// 抓取URL并把它通报给浏览器curl_exec($ch);// 关闭cURL资源,并开释系统资源curl_close($ch);

a.com/data.html利用Ajax要求同源的getdata.php:

var xhr = new XMLHttpRequest();xhr.open("GET", "getdata.php");xhr.onreadystatechange = function(){if(xhr.readyState == 4 && xhr.status == 200){console.log(xhr.responseText);}};xhr.send(null);

基于iframe实现跨域:

基于iframe实现的跨域哀求两个域属于同一个根域,如:www.a.com和b.a.com其利用同一协议(例如都是 http)和同一端口(例如都是80),此时在两个页面中同时设置document.domain为同一个主域,就实现了同域,从而可以实现通信;如b.a.com中的iframe.html:

<h1>iframe</h1><img src="images/hu.png" /><script>document.domain = "a.com";function show(msg){alert("收到的:" + msg);}if(parent.parentFun){parent.parentFun();}</script>

www.a.com主页面为:

<script>function parentFun(){alert("parentFun");}</script><iframe src="http://b.a.com/iframe.html" id="myframe"></iframe><script>var myframe = document.getElementById("myframe");document.domain = "a.com";myframe.onload = function(){var win = myframe.contentWindow;console.log(win);win.show("零点程序员");var doc = myframe.contentDocument;console.log(doc);}</script>

利用window.name和iframe进行跨域:

window的name属性返回的是该window的名称,它的值有个特点:在不同的页面(乃至不同域名)加载后依旧存在,并且可以支持非常长的name值(2MB),即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name,每个页面对window.name都有读写的权限;

正由于window的name属性的这个特色,以是可以利用window.name来进行跨域;例如a.html:

<h1>a.html</h1><script>window.name = "页面a设置的name值";setTimeout(function(){window.location = "b.html";},3000); // 3秒后在当前window中载入新的页面</script>

b.html:

<h1>b.html</h1><script>alert(window.name); // 页面a设置的name值</script>

跨域:例如,有一个a.com/a.html页面,须要通过js来获取位于另一个不同域上的页面,如:b.com/b.html里的数据:

<script>window.name = "b.com/b.html中的数据";</script>

如果b.html不跳转,其他页也可以获取数据,可以采取iframe;如a.com/a.html:

<h1>a.html</h1><iframe id="iframe" src="http://www.b.com/b.html" style="display:none"></iframe><script>var iframe = document.getElementById("iframe");// iframe在一开始载入b.com/b.html会实行此函数iframe.onload = function(){// 当iframe.src为b.html时触发,此时iframe和当前页面已经同源,可以访问iframe.onload = function(){var data = iframe.contentWindow.name;alert(data);};// 这里的b.html为随便一个页面,只要与当前页面同源就可以,// 目录是让iframe与当前页面同源iframe.src = "b.html";}</script>

利用location.hash+iframe跨域:

假设a.com/a.html要向b.com/b.html通报信息;如a.com/a.html:

<h1>a.html</h1><script>function checkHash(){try{var data = location.hash ? location.hash.substring(1) : '';console.log("收到的数据是:" + data);}catch(e){}}setInterval(checkHash, 5000);window.onload = function(){var iframe = document.createElement("iframe");// iframe.style.display = "none";iframe.src = "http://www.b.com/b.html#param"; // 通报的location.hashdocument.body.appendChild(iframe);};</script>

b.com/b.html:

<h1>b.html</h1><script>function checkHash(){var data = "";// 仿照一个大略的参数处理操作switch(location.hash){case "#param":data = "somedata";break;case "#other":// ...break;default:break;}data && callBack("#" + data);}function callBack(hash){// ie、chrome的安全机制无法修正parent.location.hash//以是要利用一个中间的www.csdnblogs.com域下的代理iframevar proxy = document.createElement("iframe");proxy.style.display = "none";proxy.src = "http://www.a.com/c.html" + hash;// 把稳该文件在a.com中document.body.appendChild(proxy);}window.onload = checkHash;</script>

a.com/c.html:

<script>//由于parent.parent和自身属于同一个域,以是可以改变其location.hash的值parent.parent.location.hash = self.location.hash.substring(1);</script>

图像Ping:

利用<img>标签,也可以动态创建图像,利用它们的onload和onerror事宜处理程序来确定是否吸收到了相应;例如:

var img = new Image();img.onload = img.onerror = function(){console.log("Done");};img.src = "https://www.zeronetwork.cn/study/pingimg.php?name=wangwei";pingimg.php:if($_GET['name']){echo $_GET['name'];}

图像Ping有两个紧张的缺陷,一是只能发送GET要求,二是无法访问做事器的相应文本,因此,图像Ping只能用于浏览器与做事器间的单向通信;提交的数据是通过查询字符串形式发送的,但相应可以是任意内容,但常日是像素图或204相应;通过图像Ping,浏览器得不到任何详细的数据,但通过侦听load和error事宜,它能知道相应是什么时候吸收到的,此时可以实现一些自身的逻辑;示例:图像Ping最常用于跟踪用户点击页面或动态广告曝光次数,如:

<input type="button" id="btn" value="图像Ping要求" /><div id="result"></div><script>var increment = (function(){var counter = 0;return function(){return ++counter;};})();var btn = document.getElementById("btn");btn.addEventListener("click", function(event){var sum = increment();var result = document.getElementById("result");var img = result.getElementsByTagName("img")[0];if(!img)img = new Image();img.onload = img.onerror = function(){result.appendChild(img);var oSpan = document.getElementById("sum");if(!oSpan){oSpan = document.createElement("span");oSpan.id = "sum";}oSpan.innerHTML = "发送要求的次数:" + sum;result.appendChild(oSpan);};if(sum % 2)img.src = "https://www.zeronetwork.cn/study/images/ad1.jpg?sum=" + sum;elseimg.src = "https://www.zeronetwork.cn/study/images/ad2.jpg?sum="+sum;</script>

基于<script>标签实现跨域:

在某些HTML元素中,可以通过它的src属性跨域要求内容,例如img、iframe等,也便是没有跨域的限定;同样,script也可以,也便是利用script来实行跨域的javascript代码,从而实现前端跨域要求数据的目的;例如:

<script>var script = document.createElement('script');script.src = "http://www.b.com/scripts/demo.js";document.body.appendChild(script);script.onload = function(){show("从a.com传过去的数据");}</script>

b.com/scripts/demo.js:

function show(msg){alert("收到的数据:" + msg);}alert("www.b.com/script/demo.js");

JSONP:JSONP是JSON with padding(添补式JSON或参数式JSON)的简写,是运用JSON的新方法,其利用<script>标签没有跨域限定的特点,可以得到从其他源动态产生的JSON数据,但JSONP要求一定须要对方做事器的支持才可以;JSONP看起来与JSON差不多,是被包含在函数调用中的JSON,形如:callback({“name”: “wangwei”});JSONP由两部分组成:回调函数callback和json数据;回调函数是当相应到来时该当在页面中调用的函数,其名字一样平常是在要求中指定的,须要在本地实现;而数据便是传入回调函数中的JSON数据;全体JSONP便是一个标准的JavaScript语句;JSONP后端做事:被要求做事端程序必须供应JSONP的做事,一样平常情形下,其会返回标准的JSONP;例如,被要求做事端b.com/jsonptest.php返回JSONP的大略形式:

<?phpecho 'alert({"name":"王唯","age":18})';

在要求端中利用<script>引入该文件,如:

<script src="http://www.b.com/jsonptest.php"></script>

要求端和做事端共同约定利用自定义函数,而不是内置函数;例如b.com/jsonptest.php:

echo 'handlerJSONP({"name":"王唯","age":18})';

要求端实现handlerJSONP()函数,并利用<script>引入b.com/jsonptest.php文件,如:

<script>function handlerJSONP(response){alert("姓名:" + response.name + "\n" + "年事:" + response.age);}</script><script src="http://www.b.com/jsonptest.php"></script>

一样平常来说,后端程序通过查询字符串许可客户端指定一个函数名,然后用这个函数名去添补相应,例如:

<script>function handlerJSONP(response){alert(response);}</script><script src="http://www.b.com/jsonptest.php?callback=handlerJSONP"></script>

b.com/jsonptest.php:

header('Content-type: application/json');//获取回调函数名$callback = htmlspecialchars($_REQUEST['callback']);//json数据$json_data = '["王唯","悄悄","娟子","大国"]';//输出jsonp格式的数据echo $callback . "(" . $json_data . ")";

JSONP是通过动态<script>元向来利用的,例如:

// ...var script = document.createElement("script");script.src = "http://www.b.com/jsonptest.php?callback=handlerJSONP";document.body.insertBefore(script, document.body.firstChild);

要求端与后端就该函数名有个约定,利用随机名,如:hander1234();而查询字符串的名字一样平常约定利用”json”或”callback”,当然也可以是其他任意的名称;

JSONP之以是在开拓职员中极为盛行,紧张缘故原由是它非常大略易用;与图像Ping比较,它的优点在于能够直接获取相应文本,支持在浏览器与做事器之间双向通信;

在某些时候,处理完JSONP数据后,动态创建的<script>就可以删除了;

function clickHandler(e){var script = document.createElement("script");script.id = "jsonp_script";script.src = "http://www.a.com/jsonp.php?callback=handlerResponse";document.body.insertBefore(script, document.body.firstChild);}function handlerResponse(response){console.log(response);var script = document.getElementById("jsonp_script");script.parentNode.removeChild(script);}var btn = document.getElementById("btn");btn.addEventListener("click", clickHandler);

定义将被脚本实行的回调函数

getJSONP[cbnum] = function(response){try{callback(response);}finally{delete getJSONP[cbnum];script.parentNode.removeChild(script);}};script.src = url;document.body.appendChild(script);}getJSONP.counter = 0; // 用于创建唯一回调函数名称的计数器function handlerResponse(response){console.log(response);}getJSONP("http://www.a.com/jsonp.php", handlerResponse);

示例:某接口的运用:

<input type="button" id="btn" value="jsonp要求"><script>var btn = document.getElementById("btn");btn.addEventListener("click", function(){var script = document.createElement("script");script.id = "jsonscript";script.src = "https://suggest.taobao.com/sug?code=utf-8&q="+ encodeURIComponent("衣服") +"&callback=jsonpCallback";document.body.appendChild(script);});function jsonpCallback(response){console.log(response);var script = document.getElementById("jsonscript");script.parentNode.removeChild(script);}</script>

JSONP的不敷:

首先,JSONP是从其他域中加载代码实行,如果其他域不屈安,很可能会在相应中夹带一些恶意代码;因此在利用不是你自己运维的Web做事时,一定得担保它安全可靠;其次,只能进行GET要求;末了,要确定JSONP要求是否失落败比较麻烦,虽然HTML5给<script>元素新增了一个onerror事宜处理程序,但目前还没有得到任何浏览器支持;

JSONP和AJAX比拟:JSONP和AJAX相同,都是客户端向做事器端发送要求,从做事器端获取数据的办法;但AJAX属于同源策略,采取CORS方案跨域,而JSONP属于非同源策略进行跨域要求;

window.postMessage()方法:

window.postMessage()方法可以安全地实现跨域通信;其是HTML5规范供应的一种受控机制来规避跨域安全限定的方法,采取异步的办法进行有限的通信,既可以用于同域通报,也可以用于跨域通报;

其运用的场景是:

页面和其打开的新窗口的数据通报;多窗口之间通报;页面与嵌套的iframe通报;

一个窗口可以得到对另一个窗口的引用,比如iframe的contentWindow属性、实行window.open返回的窗口工具、或者是命名过或数值索引的window.frames,然后在窗口上调用targetWindow.postMessage()方法分发一个MessageEvent;吸收的窗口触发onmessage事宜,并吸收分发过来的;

例如a.com/post.html,获取iframe:

<h1>a.com/post.html</h1><iframe id="iframe" src="http://www.b.com/message.html"></iframe><script>var iframe = document.getElementById("iframe");var win = iframe.contentWindow;console.log(win);</script>

b.com/ message.html:

<h1>b.com/message.html</h1>

语法:otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow为其他窗口的一个引用;message参数:将要发送到其他window的数据;它将会被构造化克隆算法序列化,即可以不受限定的将数据工具安全的传送给目标窗口而无需自己序列化;targetOrigin:通过窗口的origin属性来指定哪些窗口能吸收到事宜,其值可以是字符串""(表示无限制)或者一个URI;transfer参数:可选,是一串和message同时通报的Transferable工具,这些工具的所有权将被转移给的吸收方,而发送一方将不再保有所有权;

例如:a.com/post.html:

// ...win.postMessage("来自a.com/post.html的","");b.com/message.html:window.addEventListener("message", function(event){console.log(event); // MessageEvent});

须要延迟实行postMessage()方法,延迟的办法有多种,利用setTimeout()、iframe的onload事宜、发送者的onload事宜或者利用按钮的click事宜处理程序,如a.com/post.html:

setTimeout(function(){win.postMessage("来自a.com/post.html的","");},500);// 或:var iframe = document.getElementsByTagName("iframe")[0];iframe.onload = function() {var iframe = document.getElementById("iframe");var win = iframe.contentWindow;win.postMessage("来自a.com/post.html的","");}// 或:var btnSend = document.getElementById("btnSend");btnSend.addEventListener("click", function(){var iframe = document.getElementsByTagName("iframe")[0];var win = iframe.contentWindow;win.postMessage("来自a.com/post.html的","");});

利用postMessage()将数据发送到其他窗口时,最好指定明确的targetOrigin,而不是,缘故原由是恶意网站可以在用户不知情的情形下变动窗口的位置,乃至可以拦截所发送的数据;

win.postMessage("来自a.com/post.html的","http://www.b.com"); // 或win.postMessage("来自a.com/post.html的","http://www.b.com/"); // 或win.postMessage("来自a.com/post.html的","http://www.b.com/error.html");

在发送的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin的值,那么就不会被发送;只有三者完备匹配,才会被发送;这个机制用来掌握可以发送到哪些窗口。

MessageEvent接口:代表一段被目标工具吸收的;属性:

data属性:其保存着由发送者发送的字符串数据;lastEventId属性:表示事宜的唯一ID;origin属性:返回一个表示发送者来源;ports属性:MessagePort工具数组,表示正通过特定通道(数据通道)发送的干系端口;source属性:是一个MessageEventSource工具,代表发送者;

window.addEventListener("message", function(event){console.log(event);console.log(event.data); // 来自a.com/post.html的console.log(event.lastEventId); // 空console.log(event.origin); // http://www.a.comconsole.log(event.ports); // []console.log(event.source); // window http://www.a.com/post.html});

如果不肯望吸收message,就不要实现message事宜;如果希望从其他网站吸收message,最好利用origin或source属性验证发送者的身份;如:

window.addEventListener("message", function(event){if(event.origin != "http://www.a.com")return;console.log(event.data);});

虽然postMessage()是单向的通信,但可以利用source属性在具有不同origin的两个窗口之间建立双向通信;例如a.com/message.html:

<iframe id="iframe" src="http://www.b.com/message.html"></iframe><input type="button" id="btn" value="send" /><script>var btn = document.getElementById("btn");btn.addEventListener("click", function(){var iframe = document.getElementById("iframe");var win = iframe.contentWindow;win.postMessage("来自a.com/post.html的","http://www.b.com/");});window.addEventListener("message", function(event){console.log("a.com收到:" + event.data);});</script>

b.com/message.html:

window.addEventListener("message", function(event){if(event.origin != "http://www.a.com")return;console.log("b.com收到:" + event.data);event.source.postMessage("b.com/message回发的", event.origin);});

与利用open()方法打开的窗口通信:在向利用open()方法打开的窗口发送时,须要延迟实行,如:

btnOpen.addEventListener("click", function(){var win = window.open("http://www.b.com/message.html","_blank","width:600px,height:400px");setTimeout(function(){win.postMessage("从a.com中打开","http://www.b.com");},1000);});window.addEventListener("message", function(event){console.log("a.com收到:" + event.data);});

或者反向发送,例如a.com/post.html:

var btnOpen = document.getElementById("btnOpen");btnOpen.addEventListener("click", function(){var win = window.open("http://www.b.com/message.html","_blank","width:600px,height:400px");});window.addEventListener("message", function(event){console.log("a.com收到:" + event.data);});

b.com/message.html:

<script>window.onload = function(){var targetWindow = window.opener;console.log(targetWindow);targetWindow.postMessage("message.html is ready","http://www.a.com");}window.addEventListener("message", function(event){console.log("b.com收到:");console.log(event.data);event.source.postMessage("b.com/message.html回发的", event.origin);});</script>

postMessage()还可以发送构造化数据,该数据将会自动被(构造化克隆算法)序列化,如:

var btn = document.getElementById("btn");btn.addEventListener("click", function(){var iframe = document.getElementById("iframe");var win = iframe.contentWindow;var person = {name:"wanwei",sex: true,age: 18,friends: ["jingjing","daguo"],// smoking: function(){console.log(this.name)}, // 非常 could not be cloned.other1: undefined,other2: null}win.postMessage(person,"http://www.b.com/");

b.com/message.html:

window.addEventListener("message", function(event){console.log("b.com收到:");console.log(event.data);});

由于是跨域,以是纵然取得外源窗口的window工具,也无法操为难刁难方的DOM,但是通过吸收到的,自行构建DOM;

例如a.com/post.html:

var btn = document.getElementById("btn");btn.addEventListener("click", function(){var iframe = document.getElementById("iframe");var win = iframe.contentWindow;var person = {name:"wanwei",sex: true,age: 18,friends: ["jingjing","daguo"],}win.postMessage(person,"http://www.b.com/");});window.addEventListener("message", function(event){if(event.origin == "http://www.b.com"){if(event.data.state){document.getElementById("btn").setAttribute("disabled",true);// ...}}});b.com/message.html:window.addEventListener("message", function(event){if(event.origin == "http://www.a.com"){var person = event.data;var h1 = document.createElement("h1");h1.innerText = person.name + "信息";document.body.appendChild(h1);var p = document.createElement("p");p.innerHTML = "性别:" + (person.sex ? "男" : "女");p.innerHTML += "<br/>年事:" + person.age;p.innerHTML += "<br/>朋友:" + person.friends.join(",");document.body.appendChild(p);event.source.postMessage({state:1}, event.origin);}});

示例,后台管理的运用,main.html:

<style>{margin:0; padding:0;}ul,li{list-style: none;}.top{width:100%; height:100px; background-color:yellowgreen;}.left{width:20%; height: 100%; float: left; background-color:yellow;}.left ul li{padding:10px;}.left ul li a{color:#000; text-decoration: none;}iframe{width:80%; float: right; border:none;}.bg{position: fixed; left:0; top: 0; display: none;width:100%; height:100%; background-color: rgba(0, 0, 0, .5);}.showBg{display: block !important}.confirm{position:fixed; width:400px; height:200px; z-index: 2;left: 50%; top: 50%; transform: translate(-50%, -50%);padding: 20px; text-align: center; background-color: #FFF;}</style><div class="top"></div><div class="left"><ul><li><a href="console.html" target="iframe">掌握台</a></li><li><a href="content.html" onclick="sendMessage()" target="iframe">系统设置</a></li></ul></div><iframe name="iframe" src="console.html"></iframe><div class="bg"><div class="confirm"><p>是否确认保存?</p><p><input type="button" id="btnCancel" value="取消" /><input type="button" id="btnSave" value="保存" /></p></div></div><script>var iframe = null;window.onload = function(){var leftDiv = document.getElementsByClassName("left")[0];iframe = document.getElementsByTagName("iframe")[0];iframe.style.height = leftDiv.style.height = (document.documentElement.scrollHeight - 100) + "px";}function sendMessage(){iframe.onload = function(){iframe.contentWindow.postMessage({method: 'dataId',data: {dataId: 1}}, "");}}window.addEventListener("message", function(event){if(event.data.method == "showBg"){document.getElementsByClassName("bg")[0].classList.add("showBg");}});var btnCancel = document.getElementById("btnCancel");btnCancel.addEventListener("click", function(){if(iframe){iframe.contentWindow.postMessage({method: "cancel"}, "");document.getElementsByClassName("bg")[0].classList.remove("showBg");}});var btnSave = document.getElementById("btnSave");btnSave.addEventListener("click", function(){if(iframe){iframe.contentWindow.postMessage({method: "save"}, "");document.getElementsByClassName("bg")[0].classList.remove("showBg");}});</script>

console.html:

<h1>掌握台</h1>

content.html:

<style>.container{width:60%; margin: 0 auto;}</style><div class="container"><p>设置1:<input type="text" /></p><p>设置2:<input type="text" /></p><p>设置3:<input type="text" /></p><p>设置4:<input type="text" /></p><p><input type="button" id="btnConfirm" value="保存" /></p></div><script>window.addEventListener("message", function(event){if(event.data.method == "dataId"){console.log(event.data.data.dataId);}else if(event.data.method == "cancel"){console.log("取消操作");}else if(event.data.method == "save"){console.log("保存成功");}});var btnConfirm = document.getElementById("btnConfirm");btnConfirm.addEventListener("click", function(){window.parent.postMessage({method: "showBg"}, "");});</script>

示例,修正信息:

content.html:<div class="container"><h1>用户信息</h1><p>ID:001</p><p>姓名:<span id="usernameSpan">王唯</span><input type="button" id="editInfo" value="修正个人信息" /> </p><p>单位:<span id="jobSpan">零点网络</span><input type="button" id="editJob" value="修正事情信息" /></p><p>地址:<input type="text" id="address" value="北京市东城区" /></p><p>电话:<input type="text" id="tel" value="13888888888" /></p><p><input type="button" id="btnConfirm" value="保存" /></p></div><script>window.addEventListener("message", function(event){if(event.data.method == "dataId"){console.log(event.data.data.dataId);}else if(event.data.method == "cancel"){console.log("取消操作");}else if(event.data.method == "save"){document.getElementById("usernameSpan").innerText = event.data.data.username;console.log("保存成功");}});var btnConfirm = document.getElementById("btnConfirm");btnConfirm.addEventListener("click", function(){window.parent.postMessage({method: "showBg"}, "");});var editInfo = document.getElementById("editInfo");editInfo.addEventListener("click", function(){var win = window.open("editInfo.html", "_blank","width:200px,height:500px");setTimeout(function(){win.postMessage({method: "info",dataId: 2});},500);});</script>

editInfo.html:

<form><input type="hidden" id="ID" name="ID" /><p>姓名:<input type="text" id="username" name="username" /></p><p>性别:<input type="radio" id="male" name="sex" value="1" />男<input type="radio" id="female" name="sex" value="0" />女</p><p>年事:<input type="text" id="age" name="age" /></p><p><input type="button" id="btnCancel" value="取消" /><input type="button" id="btnSave" value="保存" /></p></form><script>window.addEventListener("message", function(event){if(event.data.method == "info"){// Ajax要求,从数据库中取出这条记录var id = event.data.dataId;var xhr = new XMLHttpRequest();xhr.open("GET", "getInfo.php?action=showInfo&ID=" + id);xhr.onreadystatechange = function(){if(xhr.readyState == 4 && xhr.status == 200){// console.log(xhr.response);var person = xhr.response;document.forms[0].elements['ID'].value = person.ID;document.forms[0].elements['username'].value = person.username;document.forms[0].elements['sex'].value = person.sex;document.forms[0].elements['age'].value = person.age;xhr = null;}};xhr.responseType = "json";xhr.send(null);}});var btnCancel = document.getElementById("btnCancel");btnCancel.addEventListener("click", function(){window.opener.postMessage({method: "cancel"}, "");window.close();});var btnSave = document.getElementById("btnSave");btnSave.addEventListener("click", function(){// 保存到数据库var data = new FormData(document.forms[0]);data.append("action", "update");var xhr = new XMLHttpRequest();xhr.open("POST", "getInfo.php");xhr.onreadystatechange = function(){if(xhr.readyState == 4 && xhr.status == 200){var data = JSON.parse(xhr.responseText);if(data.status){var person = {ID: document.forms[0].elements['ID'].value,username: document.forms[0].elements['username'].value,sex: document.forms[0].elements['sex'].value,age: document.forms[0].elements['age'].value};window.opener.postMessage({method: "save",data: person});}xhr = null;}};xhr.send(data);window.close();})</script>

editInfo.php:

<?phprequire_once "conn.php";if(isset($_REQUEST['action'])){if($_REQUEST['action'] == 'showInfo'){$ID = intval($_GET['ID']);$sql = "select ID,username, sex, age from users where ID=$ID";$result = $conn->query($sql);$row = mysqli_fetch_array($result);echo json_encode($row);}elseif($_REQUEST['action'] == 'update'){$ID = intval($_POST['ID']);$username = $_POST['username'];$sex = $_POST['sex'];$age = $_POST['age'];$sql = "update users set username='$username', sex='$sex', age='$age' where ID=$ID";$result = $conn->query($sql);if($result){echo '{"status": 1}';}else{echo '{"status": 0}';}}

超链接打开的窗口也可利用postMessage()方法进行通信,但须要设置a标签的target属性为自定义值,如:

<p><a href="http://www.b.com/content.html" target="mywin">零点程序员</a></p>b.com/content.html:console.log(window.opener); // Window or globalconsole.log(window.name); // "mywin"

发送,b.com/content.html:

if(window.opener){console.log(window.opener);window.opener.postMessage("我已经打开了","");}

主页面:

window.addEventListener("message", function(event){console.log(event.data);});

标签:

相关文章

介绍直播新纪元,轻松进入直播的五大步骤

随着互联网技术的飞速发展,直播行业在我国逐渐崛起,越来越多的人选择通过直播这一新兴媒介展示自己、分享生活、传递价值。对于许多新手来...

网站推广 2025-01-03 阅读1 评论0

介绍相机美颜原理,科技与美学的完美结合

随着科技的发展,智能手机的摄像头功能日益强大,美颜相机成为了许多人拍照的首选。美颜相机不仅满足了人们对于美的追求,更在视觉上给人带...

网站推广 2025-01-03 阅读1 评论0

介绍磁铁的制造,科学与艺术的完美结合

磁铁,一种神秘的物质,自古以来就吸引了无数人的目光。它不仅具有独特的磁性,还能在工业、医疗、科研等领域发挥重要作用。磁铁是如何制造...

网站推广 2025-01-03 阅读1 评论0

介绍电瓶激活方法,让电池焕发新生

随着科技的不断发展,电动汽车逐渐成为人们出行的首选。而电瓶作为电动汽车的核心部件,其性能直接影响着车辆的续航里程和行驶体验。新购买...

网站推广 2025-01-03 阅读1 评论0