跨域和jsonp

前言

在开发过程中往往会遇到js跨域问题,在面试中跨域几乎是必考题。js跨域有很多方式,jsonp只是其中一种,许多人对jsonp似懂非懂,下面说一下js跨域原因以及jsonp原理。

同源策略

为安全考虑,Netscape提出了同源策略 Same-Origin-Policy(SOP)。url组成包括协议名,子域名,主域名,端口号只要协议,域名,端口有任何一个的不同,就被认为是跨域,即禁止页面加载或执行与自身来源不同的域的任何脚本。即使是 localhost:8080 请求 127.0.0.1:8080 也会被认为是跨域。
实现跨域有很多种方法:

  1. 服务端代理
  2. 服务端返回响应头Access-Control-Allow-Origin
  3. jsonp
  4. iframe嵌入页面
  5. html5 postMessage

    jsonp原理

    我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。
    浏览器中script、img、iframe、link这些包含 src 属性的标签可以加载跨域资源。但浏览器限制了JavaScript的权限使其不能读、写加载的内容。
    简单来说,jsonp就是动态添加script标签引入src来实现跨域。

原生js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//客户端
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.src = src;
document.body.appendChild(script);
};
//如:在onload后,跨域请求
window.onload = function () {
addScriptTag('http://127.0.0.1:8080?callback=callback');
};
//回调的方法,且必须为全局方法,不然会报错
function callback(data) {
console.log(data);
};
//服务端 node.js
var http = require('http');
var url = require('url');
var querystring = require('querystring');
var server = http.createServer();
server.on('request', function (req, res) {
console.log(url.parse(req.url));
var urlPath = url.parse(req.url).pathname;
var qs = querystring.parse(req.url.split('?')[1]);
console.log(qs);
if (qs.callback) {
res.writeHead(200, {'Content-Type': 'application/json;charset=utf-8'});
var data = {
"name": "hugh dai"
};
data = JSON.stringify(data);
var callback = qs.callback+'('+data+');';
res.end(callback);
}
else {
res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
res.end('Hell World\n');
}
})
server.listen('8080');
console.log('Server running...');

jquery实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$.ajax({
type: "get",
async: false,
url: "http://127.0.0.1:8080",
dataType: "jsonp",
jsonpCallback:"callback", //callback函数(jsonp回调函数,默认是callback)
success: function(data){
//如果有callback函数的话两者都会执行
console.log(data);
},
error: function(){
console.log('fail');
}
});