最早的跨域方法之一就是使用这个,任何域的<img>和<script>元素都可以随便加载。
var img = new Image();
img.onload = img.onerror = function(){
alert(“Done!”);
};
img.src = “http://www.example.com/test?name=Nicholas”;
数据通过查询字符串的形式发送,返回的可能是任何东西,通常是一像素的图片或者204响应。缺点就是只能使用get方式并且无法获得响应数据。所以通常用于单向从浏览器发送数据给服务器。
JSONP(动态生成script)
全称JSON with padding ,是JSON的变形,它看起来就像JSON除了数据被一个函数调用的东西包裹着:
callback({ “name”: “Nicholas” });
当收到响应的时候,就会调用页面上的callback函数。因为JSONP是有效的JavaScript,当JSONP的响应输出到页面上后就会立刻执行。
function handleResponse(response){
alert(“You’re at IP address “ + response.ip + “, which is in “ +
response.city + “, “ + response.region_name);
}
var script = document.createElement(“script”);
script.src = “http://freegeoip.net/json/?callback=handleResponse”;
document.body.insertBefore(script, document.body.firstChild);
比起用image,可以从响应里获得数据了。问题是可以注入邪恶脚本。其次是很难检测是否失败了,虽然HTML5给script定义了onerror事件,但浏览器支持不好。只能用定时检测在一段时间里是否受到了响应
Comet
服务器给页面推送数据,而不是页面向服务器请求数据,这种方式让信息能够更接近实时,例如比赛分数,股票价格等。
comet有实现两种方式,long polling和streaming
long polling
相对于传统的polling(short polling),浏览器定时请求服务器看有没有新数据,服务器不管有没有可用数据都马上返回响应。而long polling等待有可用数据的时候才返回响应,浏览器收到响应后马上发起新的请求。
优点就是使用xhr对象配合setTimeOut就可以了。只需要管理什么时候发送请求。
第二种方式是HTTP streaming
它是指使用单一个请求在整个页面的生存周期里,浏览器向服务器发送一个请求然后服务器保持这个链接不断,周期性地向浏览器推送数据。因为服务器端的语言都支持把数据写到输出缓存里,然后flush给客户端,因此这就是HTTP streaming的核心了。
举例:
function createStreamingClient(url, progress, finished){
var xhr = new XMLHttpRequest(),
received = 0;
xhr.open(“get”, url, true);
xhr.onreadystatechange = function(){
var result;
if (xhr.readyState == 3){
//get only the new data and adjust counter
result = xhr.responseText.substring(received);
received += result.length;
//call the progress callback
progress(result);
} else if (xhr.readyState == 4){
finished(xhr.responseText);
}
};
xhr.send(null);
return xhr;
}
var client = createStreamingClient(“streaming.php”, function(data){
alert(“Received: “ + data);
}, function(data){
alert(“Done!”);
});
1、server-sent events
这是一个针对只读comet交互的接口,这个接口建立了和服务器的单向链接(当然你可以配合xhr来实现双向通信),服务器响应的时候必须包含一个MIME类型为text/event-stream,输出的信息以一种指定的给浏览器。这个接口支持short polling,long polling和HTTP streaming,它会自动重新连接当原连接断开时。
var source = new EventSource(“myevents.php”);
地址必须是同域的,和调用该对象所在域一致,该对象实例也有readystate属性:
0 代表正在连接服务器
1 代表链接打开
2 代表链接关闭
有三个事件:
open 当链接建立时触发
message 当从服务器收到新事件时触发
error 当无法建立连接触发
source.onmessage = function(event){
var data = event.data; 从服务器传回来的信息以字符串的形式保存在data属性里
//do something with the data
};
默认地,当和服务器的连接被关闭, EventSource 会重新连接,可以通过close方法断开连接,禁止重新连接
source.close();
服务器发送的事件带上MIME类型为 text/event-stream,以long-lasting HTTP响应返回。
格式如下:
data: foo
data: bar
data: foo
data: bar
每一个空格代表发送一个message事件,例如第三个message事件event.data 值为 “foo\nbar”
可以指定给事件加个ID标识:
data:foo
id: 1
这样在多个连接情况下,断开重连也能找到之前的位置咯
2、Web socket
目的是通过一个long-lasting链接来实现浏览器和客户端的双向沟通。
当建立这种连接的时候,一个HTTP请求发送给服务器初始化一个链接,当服务器响应时,该连接会从HTTP升级为Web socket协议。这意味着服务器需要是支持Web socket 协议的。
web socket普通连接为 ws://
安全连接为 wss://
好处是发送很小的数据,不受HTTP字节妨碍,使用小数据包让Web socket在移动应用很适合。
var socket = new WebSocket("ws://www.haha.com/server.php");
必须传入绝对地址,可以跨域,完全由服务器决定是否和请求域建立连接。
浏览器会尝试建立连接当WebSocket对象被实例化以后。类似xhr,它也有一个readystate属性,只是值不同:
0 正在建立连接
1 连接已经建立好
2 连接开始关闭
3 连接已经关闭
Websocket是没有readystatechange事件的,但有相对不同状态的处理事件。你可以使用close来关闭链接
socket.close();
调用close,readystate马上变为2最后变为3
发送数据给服务器:
socket.send(“haha");
因为Web socket只能发送plain text,你需要把复杂数据结构先序列化:
var message = {
time: new Date(),
text: “Hello world!”,
clientId: “asdfp8734rew”
};
socket.send(JSON.stringify(message));
当服务器像客户端发送信息时,会触发message事件,返回的数据也是字符串,需要手动解析:
var data = event.data;
//do something with data
};
Web socket只能用Dom 0事件添加方式:
socket.onopen = function(){
alert(“Connection established.”);
};
socket.onerror = function(){
alert(“Connection error.”);
};
socket.onclose = function(){
alert(“Connection closed.”);
};
只有close事件有额外信息在event对象里:
wasClean属性: 布尔值,表示链接是否被干净地关掉
code:服务器返回的状态代码,数字
season:服务器发送的字符串
socket.onclose = function(event){
console.log(“Was clean? “ + event.wasClean + “ Code=” + event.code + “ Reason=”
+ event.reason);
};
安全问题
1、浏览器和服务器可以接入修改URL,例如:/getuserinfo.php?uid=6,没办法阻止别人把uid改成别的,因此 getuserinfo.php需要做进一步验证
2、当一个未经授权的系统能够进来获取资源的时候,就意味着发生了跨网站请求伪造(csrf)攻击。
预防就是验证该请求发送发之前接入过这个资源:
方法1:要求请求资源时带有SSL证书
方法2:要求每个请求带上一个用来计算的taken
document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://script.a.com/b.html';
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在这里操纵b.html
alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
};