出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。(同源策略主要针对域名host,端口号part)通俗来讲,因为安全原因 服务器只允许浏览器在本域下的文档资源,若非本域访问,则会产生跨域。
一、跨域案例
1.通过Node构建两个web服务器
2.将页面挂载到服务器1
let http = require('http');
let fs = require('fs');
http.createServer((req,res)=>{
//创建服务1 将index.html挂载到服务器
fs.readFile("./index.html",(err,data)=>{
if(err) throw err;
res.end(data);
})
}).listen(1111);
//挂载到http://localhost:1111
console.log("success port1111");
3.创建服务器2模拟跨域请求
let http = require('http');
http.createServer((req,res)=>{
res.end("这是服务器2中的文本")
}).listen(2222);
///挂载到http://localhost:2222
console.log("success port2222");
4.结果
提示请求跨域,因为网页挂载在http://localhost:1111下,而页面向服务器2http://localhost:2222
中提交了一个get请求,他们不在同一个域下,违反了同源安全策略,即是一个跨域请求。
二、解决方法
目前常用的两种方式是CORS与JSONP;
- CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
- 普通跨域请求:只需服务器端设置Access-Control-Allow-Origin请求头即可
- 带cookie跨域请求:前后端都需要进行设置
let http = require('http');
http.createServer((req,res)=>{
res.writeHead(200,{
"Content-Type":"text/plain",
"Access-Control-Allow-Origin":"http://localhost:1111",
//"Access-Control-Allow-Origin":"*"
});
res.end("这是服务器2中的文本")
}).listen(2222);
///挂载到http://localhost:2222
console.log("success port2222");
Access-Control-Allow-Origin值有两种配置方式,* 代表允许所有域,填写特定域名则可放行特定域
获取成功,解决跨域。
- JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。其原理是利用script标签不受同源策略的限制,可以自由加载各类文件的特点去实现的;
请求配置
//创建一个script标签
let scriptTag = document.createElement("script");
//注入请求地址 可带参数
scriptTag.src = "http://localhost:2222/?callback=callback";
//将创建script标签放入head中 发送请求
document.querySelector('head').appendChild(scriptTag);
//回调函数
function callback(res) {
console.log(res)
}
服务端配置
let http = require('http');
let url = require('url');
let querystring = require('querystring');
//导入url querystring模块,解析请求参数
http.createServer((req,res)=>{
//获取参数
let query = querystring.parse(url.parse(req.url).query);
//回调函数名称
let callback = query.callback;
console.log(callback);
res.writeHead(200,{
//返回数据格式一定要为js
"Content-Type":"text/javascript;charset=UTF-8"
});
let data = { //定义返回数据
code:200,
msg:"这是一段文字!"
};
//发送数据包
res.end(`${callback}(${JSON.stringify(data)})`)
}).listen(2222);
console.log("success 2222");
请求成功,callback函数调用成功
JSONP实际上就是前端页面通过创建script标签形成跨域请求,并在页面中编写回调函数并将函数名传递给后端,等待后端响应,以 func(传参);调用的方式将数据通过js的方式返回到创建的script标签中,而返回数据被浏览器编译为js代码,到达页面后立即调用回调函数完成请求;
三、JSONP请求封装
创建一个jsonp请求的步骤相对较多,我用Promise做了一个简单的封装
代码封装
function jsonp(option){
return new Promise((resolve,reject)=>{
//创建一个script标签
let scriptTag = document.createElement("script");
//定义数据段
let query = '?';
//数据段拼接
for (key in option.data){
query += key + '=' +option.data[key] +'&'
}
//回调函数名拼接
query += 'callback=' + option.callbackName;
//填写src
scriptTag.src = option.url + query;
//插入到head中 发送请求
//动态创建callback
window[option.callbackName] = res => {
//请求jsonp接口成功后,删除该函数 - 不污染window
delete window[option.callbackName];
//删除请求接口时动态创建的script标记
document.head.removeChild(scriptTag);
//判断接口的数据返回
if (res) {
resolve(res);
} else {
reject("服务器没有返回数据");
}
};
//发送请求 一定要在动态创建回调函数后发送 所以放在最后
document.querySelector('head').appendChild(scriptTag);
})
}
调用方法
jsonp({
url:'http://localhost:2222',
data: {
},
callbackName:'callback'
}).then(res=>{
//成功方法
}).catch(res=>{
//未成功方法
})
这样用起来是不是简单很多呢!