跨域(cross origin resource sharing)
为什么要跨域?
客户端请求服务端的时候,为了保护服务器的资源不被其他人访问到,浏览器出台了同源策略。
同源策略
同源策略,它是由Netscape提出的一个著名的安全策略。所谓同源是指,域名,协议,端口相同
const url = 'http:/ /www.163. com'
不同源的示例
- https://www.163. com
- http://email.163.com
- http://www.163. com:8088
有很多时候,我们自己的客户端5服务器不同源、或者说我们想获得不同源服务器下的数据,那么跨域技术应用而生
常见域名后缀
- com 国际
- cn 中国
- uk 英国
- org
- edu 教育
常见的跨域技术
** 浏览器向服务器请求的数据**
- JSONP
- 原理:因为src属性的标签,不受同源策略的约束,我们可以通过动态创建带有src的标签,来访问服务器返回请求数据、结果
- 服务器端跨域
- 正向代理服务器
- 反向代理服务器(ngix)
jsonp 跨域
//将一个对象转成序列化字符串
function formdata(opt, callback) {
let temp = '';
for (let key in opt) {
temp += key + '=' + opt[key] + '&';
}
if (callback != undefined) {
temp += 'callback=' + callback;
}
return temp;
}
function Jsonp(url, opt, callback = 'callback') {
// 返回一个可以链式调用的promise对象
return new Promise((resolve, reject) => {
// 将前端querystring查询字符串的参数,拼接到参数栏
url = url + (url.indexOf('?') > -1 ? '' : '?') + formdata(opt, callback);
// 动态创建script标签
let ele = document.createElement('script');
// 设置接口的请求地址
ele.src = url;
//添加到页面中
document.body.append(ele);
// 设置请求jsonp接口的回调
window[callback] = result => {
// 请求jsonp接口成功后删除该函数 ---不要污染window
delete window[callback];
// 从页面中删除接口动态创建的script标签
document.body.removeChild(ele);
// 判断接口的数据返回
if (result) {
resolve(result);
} else {
reject('服务器没有返回信息');
}
};
//动态创建script标记,错误的监听
ele.addEventListener('error', () => {
// 请求jsonp接口成功后删除该函数 ---不要污染window
delete window['callback'];
// 从页面中删除接口动态创建的script标签
document.body.removeChild(ele);
});
});
}
// egg后台
// Jsonp('http://127.0.0.1:7001/api', {
// username: 'zs',
// age: 12
// }).then(result => {
// console.log(result);
// });
// express 后台
Jsonp('http://127.0.0.1:3000/api', {
username: 'zs',
age: 12
}).then(result => {
console.log(result, 'jsnop get');
});
- egg写的后台
//./app/router.js
module.exports = app => {
const { router, controller } = app;
let jsonp = app.jsonp();
//必须写成let jsonp=app.jsonp()
//不能先解构jsonp 在写到router.get上jsonp()
router.get('/seach', jsonp, controller.home.seach);
};
- express 写的后台代理
const url = require('url');
const qs = require('querystring');
app.get('/api', (req, res) => {
res.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
const ourl = url.parse(req.url);
const oQuery = qs.parse(ourl.query);
//判断是不是jsonp接口
if (oQuery.callback) {
res.end(
oQuery.callback + '(' + JSON.stringify({ code: 1, msg: 'jsonp' }) + ')'
);
} else {
res.end(JSON.stringify({ code: 1, msg: 'json' }));
}
});
自封一个ajax
function ajax(url, params = null, type = 'GET') {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open(type, url);
params = formdata(params);
if (type == 'GET' && params) {
url += url.indexOf('?') > -1 ? '' : '?' + params ;
xhr.send(null);
} else if (type == 'POST') {
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.send(params);
}
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return;
if (xhr.status == 200) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error('request failed'));
}
};
});
}
//将一个对象转成序列化字符串
function formdata(opt, callback) {
let temp = '';
for (let key in opt) {
temp += key + '=' + opt[key] + '&';
}
if (callback != undefined) {
temp += 'callback=' + callback;
}
return temp;
}
//ajax get 请求
ajax('http://127.0.0.1:3000/api', {
username: 'zs',
age: 12
}).then(res => {
console.log(res, 'ajax get');
});
//ajax post 请求
ajax(
'http://127.0.0.1:3000/postApi', {
username: 'zs',
age: 12
},
'POST'
).then(res => {
console.log(res, 'ajax post');
});
- express 写的后台代理
router.post('/postApi', (req, res) => {
res.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
const body = req.body;
res.end(JSON.stringify({ code: 1, msg: 'json post' ,...body}));
});
网友评论