什么是跨域?
我们经常会在页面上使用ajax请求访问其他服务器的数据,此时,客户端会出现跨域问题,跨域问题是由于javascript语言安全限制中的同源策略造成的。简单来说,同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合。例如:
解决跨域的几种方式:
JSONP
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。其核心思想是利用JS标签里面的跨域特性进行跨域数据访问,在JS标签里面存在的是一个跨域的URL,实际执行的时候通过这个URL获得一段字符串,这段返回的字符串必须是一个合法的JS调用,通过EVAL这个字符串来完成对获得的数据的处理。
在HTML文档中能够发起HTTP请求的元素有:< a>,< img >,< link> ,< script>,< iframe>< frame>请求HTML文档文件,< form>请求(get/post),< object>请求其他资源文件,音频视频,插件,< source>用于音频视频,从域的概念来讲,HTML中的这些元素,都是可以跨域请求资源的。这些请求都是传统的HTTP请求。
代码:
服务器端:Nodejs服务器
var http = require("http");
var https = require("https");
url = require('url');
var port = process.env.PORT || 9000;
function requestlistener(req, res) {
res.writeHead(200,{"Content-Type":"application/json"});
var otherArray = ["item1", "item2"];
var otherObject = { item1: "item1val", item2: "item2val" };
var json = JSON.stringify({
anObject: otherObject,
anArray: otherArray
});
req = url.parse(req.url, true);
if(!req.query.callback){
res.end();
}
//console.log("name is:"+req.query.name);
res.write(req.query.callback+"("+json+")");
res.end();
}
http.createServer(requestlistener).listen(port);
console.log('The HTTPS server is listening on port ' + port);
客户端:分2种形式jquery.getJSON和jquery.ajax
getJSON:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>crossDomain</title>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js" type="text/javascript"></script>
</head>
<body>
<div>
<button onclick="getjson()">click me!</button>
<p>...</p>
</div>
<script type="text/javascript">
function getjson() {
$.getJSON("http://60.205.226.32:9000/?callback=?", function(result){
console.log(result);
});
}
</script>
</body>
</html>
ajax:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>crossDomain</title>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js" type="text/javascript"></script>
</head>
<body>
<div>
<button>click me!</button>
<p>...</p>
</div>
<script type="text/javascript">
const hostname = '60.205.226.32';
const port = 9000;
function getJSon(data) {
$("p").html(JSON.stringify(data));
//document.getElementById("ppp").innerHTML=(JSON.stringify(data));
console.log(data);
}
$(function(){
$("button").bind("click",function(){
var url = "http://" + hostname + ":" + port;
$.ajax({
url:url,
crossDomain:true,
dataType:"jsonp",
jsonp:"callback",
jsonpCallback:"getJSon"
})
});
});
</script>
</body>
</html>
这两种方法,点击button的时候,从Firefox浏览器的firebug,实际上都在head里面能看到动态生成了一段script 里面有服务器的src,其实, JSONP就是利用script 的 src属性,实现跨域的功能.
AJAX:
相信这个应该不用过多的讲解了吧.
差不多就4步:
•创建xhr对象
•监听请求
•设置回调
•设置参数
•发送xhr
•获得数据执行回调
这里,我就直接上代码了.
var sendAjax = (function() {
var getXHR = (function() {
var xhr;
if(window.XHRHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveObject("Microsoft.XMLHTTP");
}
return xhr;
})();
return function(url,opts){ //url为目标地址
var xhr = getXHR(),
data;
xhr.onreadystatechange = function(){
if(xhr.readyState===4||xhr.status===200){
data = JSON.parse(xhr.responseText); //将data解析为json对象
opts.callback(data);
}
}
xhr.setRequestHeader('Content-Type','application/json');
xhr.open(opts.method,url); //写入参数
xhr.send(JSON.stringify(opts.data)); //将参数json字符化
}
})();
//调用执行
sendAjax('www.example.com',{
callback:function(data){
//...
},
data:{
name:'JIMMY',
age:18
}
})
JSONP 就是 JSON with Padding… 我真的不知道这个名字的含义到时有什么卵用
一开始在使用JSONP时, 就是使用jquery的$.ajax函数就可以了. 但,这造成了一个很不好的impression. 总是让我们以为,JSONP 和 ajax有什么关联似的. 而,事实上,他们两个是完全不同的机制。
SSE:
ajax和JSONP 都是 client-fetch的操作. 但是有时候, 我们更需要服务器主动给我们发信息. 比如,现在的APP应用,完全可以实现服务器发送, 然后Client再处理. 而,SSE就是帮助我们向webapp靠近.
SSE 全称就是 Server-Sent Events. 中译 为 服务器推送.
他的技术并不是很难,和websocket不同,他依赖原生的HTTP,所以对于开发者来说更好理解。 比如,在nodeJS, 只要我不执行res.end(),并且一定时间持续发送信息的话,那么该连接就会持续打开(keep-alive). 其实通俗来说,就是一个长连接. 所以,以前我们通常使用ajax,iframe长轮询来代替他.但是这样有个缺点就是, 可操控性弱, 错误率高。 所以,正对于这点W3C, 觉得需要在客户端另外指定一个机制–能够保证服务器推送, 实现连接的keep-alive,操作简单… 在这样背景下SSE诞生了.
但SSE和AJAX具体的区别在什么地方呢?
•数据类型不同: SSE 只能接受 type/event-stream 类型. AJAX 可以接受任意类型
•结束机制不同: 虽然使用AJAX长轮询也可以实现这样的效果, 但是, 服务器端(nodeJS)必须在一定时间内执行res.end()才行. 而SSE, 只需要执行res.write() 即可.
简单demo:
var source = new EventSource('/dates'); //指定路由发送
source.onmessage = function(e) { //监听信息的传输
var data = JSON.parse(e.data),
origin = e.origin;
};
source.onerror = function(e) { //当连接发生error时触发
console.log(e);
};
source.onopen = function(e) { //当连接正式建立时触发
console.log(e);
};
SSE主要就是创建一个EventSource对象. 里面的参数就是发送的路由, 不过目前还不支持CORS,所以也被限制在同源策略下.
在返回的source里面包含了,需要处理的一切信息.SSE也是通过事件驱动的,如上面demo所述. 这里,SSE通常有一下几类重要的事件.
上面几个方法比较重要的还是message方法. message主要用来进行信息的接受, 回调中的event 包含了返回的相关数据.
event包含的内容:
服务端使用SSE:
由于使用的是HTTP协议,所以对于服务端基本上没什么太大的改变. 唯一注意的就是, 发送数据使用res.write()即可,断开的时候使用res.end();
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Access-Control-Allow-Origin": "*" //允许跨域
});
var num =0;
var f = function(){
if(num===10){
res.end();
}else{
res.write("id: " + num + "n");
res.write("data: " + num + "nn");
num++;
}
setTimeout(f,1000);
}
f();
Ok~ 这里有一个demo, 大家可以打开控制台看一下. 会发现,有一个连接一直处于Content-Download状态. 该连接就是一个SSE。
WebSocket:
websocket 不同于其他的HTTP协议,他是独立于HTTP存在的另外一种通信协议。比如,像这样的一个路径ws://websocket.example.com/,就是一个websocket 通信. 通常的实时通信并不会传输大量的内容, 所以,对于HTTP协议那种,进行连接时需要传递,cookie和request Headers来说, 这种方式的通信协议,会造成一定的时延(latency). websocket通信协议就是在这样的背景下诞生了, 他与SSE,ajax polling不同的是–双向通信。
我们来看一个简单的websocket demo:
var socket = new WebSocket('ws://localhost:8080/');
socket.onopen = function () {
console.log('Connected!');
};
socket.onmessage = function (event) {
console.log('Received data: ' + event.data);
socket.close();
};
socket.onclose = function () {
console.log('Lost connection!');
};
socket.onerror = function () {
console.log('Error!');
};
socket.send('hello, world!');
可以说上面就是一个健全的websocket 通信了. 和SSE一样,我们需要创建一个WebSocket对象, 里面的参数指定连接的路由. 而且,他也是事件驱动的.
常见的事件监听有.
websocket 发送数据:
另外,websocket 最大的特点就是可以双向通信。这里可以使用.
ws.send()方法发送数据, 不过只能发送String和二进制. 这里,我们通常call 数据叫做 Frames. 他是数据发送的最小单元.包含数据的长度和数据内容.
下面就是几种常用的发送方式:
socket.send("Hello server!");
socket.send(JSON.stringify({'msg': 'payload'}));
var buffer = new ArrayBuffer(128);
socket.send(buffer);
var intview = new Uint32Array(buffer);
socket.send(intview);
var blob = new Blob([buffer]);
socket.send(blob);
websocket 接受数据:
ws.onmessage = function(msg) {
if(msg.data instanceof Blob) {
processBlob(msg.data);
} else {
processText(JSON.parse(msg.data)); //接受JSON数据
}
}
那server端应该怎样处理websocket通信呢?
websocket虽然是另外一种协议,不过底层还是封装了TCP通信, 所以使用nodeJS的net模块,基本就可以满足,不过里面需要设置很多的头. 这里推荐使用ws模块.
NodeJS 发送websocket数据:
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({ port: 8080 });
//通过ws+ssl的方式通信. 和HTTPS类似
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});