1. 聊聊你对跨域的理解
前端开发进化史
服务器渲染 「半服务器渲染 SSR」
客户端渲染
为啥会产生跨域
服务器分离:WEB服务器、数据服务器、图片服务器…
云信息共享:第三方API接口
有助于分离开发:开发跨域、部署同源
……
解决方案
JSONP
CORS
Proxy
……
同源策略请求 && 非同源策略(跨域)请求
HTML页面预览的地址:http://127.0.0.1:5500/index.html
基于Ajax发送请求的数据接口地址:http://127.0.0.1:9999/user/list
如果两个地址的“协议、域名、端口号”完全一致,则是同源请求,只要有一个不一样,则就是跨域请求!!
+ 同源请求:把web页面和后台的接口程序部署在“相同服务器的相同服务下”
+ 跨域请求:页面和后台分开部署「同一台服务器的不同端口下、或者不同服务器下...」
项目场景:
+ 开发的时候:我们在自己的电脑上启动服务预览项目,但是后台的程序在其他的服务器上,没有在一起,此时的数据请求就是“跨域”的!!
+ 部署到服务器:
+ 全部署在一起(相同服务器的相同服务下),此时就是同源「不建议」
+ 当代部署一般都是分服务器部署,也就是页面和后台是部署在不同服务器上的,此时就是跨域
+ ...
默认情况下不允许Ajax跨域请求,因为浏览器有“安全策略”
Access to XMLHttpRequest at 'https://www.jianshu.com/asimov...' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
服务器和服务器之间默认是没有跨域限制的
==============================
非同源策略(跨域)请求方案:
+ proxy跨域代理:原理就是利用“服务器和服务器之间不存在跨域”来实现的
步骤:
@1 在本地启动一个服务:一方面可以实现页面的预览,另一方面也可以代理转发数据请求
@2 我们以后再发数据请求,发送给本地启动的这个服务,而这个服务会去真正的服务器上获取到相关的数据,再返回给客户端!!
+ 本地开发的时候,我们基于proxy跨域代理来完成「webpack-dev-server、Node...」
+ 部署到服务器的时候,我们一般是基于nginx的反向代理来处理
+ CORS跨域资源共享:默认因为浏览器的安全策略,是不允许ajax跨域访问的,但是如果服务器设置了 Access-Control-Allow-Origin 响应头信息,设置允许这个源发送请求,那么浏览器也就不在去限制了!!
步骤:
@1 由服务器设置允许源
Access-Control-Allow-Origin 设置允许的源「白名单机制」
Access-Control-Allow-Credentials 是否允许携带资源凭证「例如:cookie」
服务器端设置了允许,则客户端也要配合设置允许
axios.defaults.withCredentials = true;
xhr.withCredentials=true;
Access-Control-Allow-Headers 设置允许的请求头
Access-Control-Allow-Methods 设置允许的请求方式
@2 在CORS跨域共享中,客户端首先会尝试发送一个OPTIONS试探请求,验证是否连接成功,连接成功后,再发送真正的请求!!
+ JSONP跨域解决方案
原理:利用了
服务器端:
/*-CREATE SERVER-*/ const express = require('express'), app = express(); app.listen(1001, () => console.log(`服务启动成功,正在监听1001端口!`)); app.get('/user/list', (req, res) => { // 获取传递进来的callback值,例如:'func' let { callback } = req.query; // 准备数据 let result = { code: 0, data: ['张三', '李四'] }; // 返回给客户端指定的格式,例如:’函数名(数据)‘ res.send(`${callback}(${JSON.stringify(result)})`); }); /* STATIC WEB */ app.use(express.static('./'));
proxy
/*-CREATE SERVER-*/ const express = require('express'), request = require('request'), app = express(); app.listen(1001, () => console.log(`服务启动成功,正在监听1001端口!`)); // 服务器接收到客户端发送过来的请求 app.get('/aaa', (req, res) => { // 向简书发送相同请求,从简书服务器获取想要的数据「不存在域的限制的」 let jianURL = `https://www.jianshu.com/asimov/subscriptions/recommended_collections`; req.pipe(request(jianURL)).pipe(res); }); /* STATIC WEB */ app.use(express.static('./'));
CORS
/*-CREATE SERVER-*/ const express = require('express'), app = express(); app.listen(1001, () => console.log(`服务启动成功,正在监听1001端口!`)); /*-MIDDLE WARE-*/ // 设置白名单 let safeList = ["http://127.0.0.1:5500", "http://127.0.0.1:3000", "http://127.0.0.1:8080", "http://127.0.0.1:5501"]; app.use((req, res, next) => { let origin = req.headers.origin || req.headers.referer || ""; origin = origin.replace(/\/$/g, ''); origin = !safeList.includes(origin) ? '' : origin; res.header("Access-Control-Allow-Origin", origin); res.header("Access-Control-Allow-Credentials", true); res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length,Authorization, Accept,X-Requested-With"); res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS,HEAD"); req.method === 'OPTIONS' ? res.send('OK') : next(); }); /*-API-*/ app.get('/list', (_, res) => { res.send({ code: 0, message: 'zhufeng' }); }); /* STATIC WEB */ app.use(express.static('./'));
vue.config.js
webpack-dev-server
const ENV = process.env.NODE_ENV; module.exports = { lintOnSave: ENV !== 'production', publicPath: './', productionSourceMap: false, // 对webpack-dev-server的配置 devServer: { // 配置跨域代理「可以配置对多台服务器的代理」 proxy: { // 所有以“/api”开始的请求,都发送到代理服务器「走这个配置」 "/api": { target: "https://www.jianshu.com/asimov", ws: true, changeOrigin: true, pathRewrite: { "^/api": "" } }, // 所有以“/zhihu”开始的请求,都代理到知乎服务器 "/zhihu": { target: "https://news-at.zhihu.com/api/4", ws: true, changeOrigin: true, pathRewrite: { "^/zhihu": "" } } } } };
部署到服务器上:基于nginx实现反向代理
处理原理
自己基于node实现
const request = require('request');
app.get('/subscriptions/recommended_collections', function (req, res) {
let url = 'https://www.jianshu.com/asimov' + req.url;
req.pipe(request(url)).pipe(res);
});
扩展:其他跨域方案「配合iframe」
postMessage
window.name
document.domin
location.hash
……
----------------------------------------