前言
- 不管是 ajax 请求、canvas 的渲染等,都有可能牵涉一个问题 - 跨域
- 跨域是因为受到了浏览器同源策略的影响
- 我们也经常遇到跨域的问题,这让我们很多功能的实现上都产生了一定影响,我们下面就来看看,如何解决跨域的问题
- 完整相册 Demo - 相册
前后端交互问题 - 跨域
浏览器同源策略
- 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源
- 同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收
- 源 :协议、域名和端口号
不受同源策略影响的资源引入
<script></script>
、<img/>
、<link></link>
、<iframe></iframe>
-src
方案
- jsonp
- CORS解决跨域
- proxy - 服务器代理请求
jsonp
- jsonp - JSON width Padding
- jsonp 的原理 - 通过
<script></script>
不受同源策略影响的特性实现跨域
简单使用
- 后端 node - koa
router.get('/getAjax', ctx => {
// 直接返还浏览器可执行的 js 代码
ctx.body = 'var a = 123';
});
- 前端
<script src="http://127.0.0.1:1000/getAjax"></script>
<script>
console.log(a); // 123
</script>
- 这么做有一些新的问题产生
- 变量名污染
- 交互是为了通信,我把数据传给你,你把数据传回给我,这么做就只是 js 引入,没有任何意义
动态创建 script 实现请求
- 我们根据之前的 简单使用 出现的问题,对 jsonp 的
script
进行修改
- 使用
params
或query
进行客户端->浏览器
的数据传输 - 根据需求动态创建
script
- 异步问题 - 请求需要时间,那么后面会有可能使得所需数据没加载进来但你却使用了导致报错
const btn = document.querySelector('button');
btn.onclick = () => {
const jsonp = document.createElement('script');
jsonp.src = `http://127.0.0.1:3000/getAjax`;
document.head.appendChild(jsonp);
// 避免异步问题,等 onload 之后再执行
jsonp.onload = () => {
console.log(a);
}
}
- onload 这种方式感觉没什么问题,但看着挺蠢的,所以我们可以利用回调再改一下,把回调的方法名传到后台,再由后端返回执行js
- 后端 node - koa
router.get('/getAjax', ctx => {
const { cb, name } = ctx.query;
ctx.body = `${cb}({
name: '${name}',
age: 20
})`;
});
- 前端
const btn = document.querySelector('button');
btn.onclick = () => {
const jsonp = document.createElement('script');
jsonp.src = `http://127.0.0.1:3000/getAjax?name=张三&cb=cbFn`;
document.head.appendChild(jsonp);
}
function cbFn(options) {
console.log(options);
}
典型例子 - 百度搜索
- 百度接口
https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=hello&cb=succFn
- wd - 搜索关键字
- cb - 回调
jsonp存在的问题
- 只能是 get 请求
- 安全性问题
CORS跨域设置
- CORS(Cross-origin resource sharing),跨域资源共享,是一份浏览器技术的规范,用来避开浏览器的同源策略
- 该设置需要后端配合
- 实际上就是后端返回数据时携带一个特殊的头信息
Access-Control-Allow-Origin
- 当我们的浏览器通过 ajax 发送了一个请求的时候,如果该请求不同源,那么浏览器会去看一下请求头信息里面是否存在一个
Access-Control-Allow-Origin
字段,且当前的域是否在该字段的值内, - 如果为真,则表示该数据是可信任的,就接收响应数据
- 否则拒绝接收
- 后端 - node - koa
ctx.set('Access-Control-Allow-Origin', 'http://127.0.0.1:8080'); // 表示只有 http://127.0.0.1:8080 可以访问
ctx.set('Access-Control-Allow-Origin', '*'); // * 为通配符,表示任何域名都可以访问
CORS 头信息设置
- CORS请求时,
XMLHttpRequest
对象的getResponseHeader()
只能拿到6个基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
- 如果想拿到其他字段,就必须在
Access-Control-Expose-Headers
里面指定 - 我们还需要通过两个特殊头信息设置
Access-Control-Allow-Headers
- 允许requset设置的头部Access-Control-Expose-Headers
- 允许客户端获取的头部key
- 后端 - node - koa
ctx.set({
'Access-Control-Allow-Origin': 'http://127.0.0.1:8080',
'Access-Control-Allow-Headers': 'Authorization',
'Access-Control-Expose-Headers': 'Authorization'
});
预检请求
-
在跨站点请求的时候,即使服务器设置了
Access-Control-Allow-Origin
,如非简单请求,也会请求失败,其原因是因为在正式请求前,会有一个预检请求 -
需预检的请求要求必须首先使用
OPTIONS
方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求 -
预检请求的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响
-
简单请求 - 满足条件:
-
method
- GET
- POST
- HEAD
-
除了被用户代理自动设置的头信息(例如
Connection
,User-Agent
和在 Fetch 规范中定义为 禁用首部名称 的其他头信息,允许人为设置的字段为 Fetch 规范定义的 对 CORS 安全的首部字段集合。该集合为:- Accept
- Accept-Language
- Content-Language
- Content-Type(需要注意额外的限制)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
-
Content-Type 的值仅限于下列三者之一:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
-
-
解决方案 - 后端允许预检通过
-
后端 - node - koa
if(ctx.method === 'OPTIONS') {
ctx.set('Access-Control-Request-Method', 'POST');
return ctx.body = ''
}
后端代理
-
后端 即能接电话(响应-提供服务),还能打电话(发送请求)
-
跨域问题是由浏览器的同源策略这个安全机制引起的,我们就可以通过服务器请求数据,不由浏览器直接请求数据,解决跨域问题
-
即由同源服务器作为代理,访问其他服务器:浏览器 -> 同源服务器 -> 非同源服务器 -> 同源服务器 -> 浏览器
-
node原生
let data;
const options = {
protocol: 'http:',
hostname: '127.0.0.1',
port: 3000,
path: url.replace(/^\/api/, ''),
method,
headers,
}
const req = http.request(options, res => {
res.on('data', chunk => {
data += chunk.toString();
console.log(`BODY: ${chunk}`);
})
res.on('end', () => {
res.write(data);
console.log('完成');
})
})
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.end();
- koa-server-http-proxy
app.use(proxy('/api', {
target: 'http://127.0.0.1:3000',
pathRewrite: {
'^/api': ''
}
}));
代理方式
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!