系列文章:javascript
在系列文章的第一篇咱们谈到过跨域问题产生的缘由是浏览器的同源策略. 那么服务器之间通讯就不会受到相关条件的限制. 那么是否是咱们能够经过同域服务器帮助访问其余域名的 api 呢? 若是能够的话, 那岂不是能够想访问谁就访问谁? 限制, 不存在的...html
ps: 本文涉及到部分后端知识, 须要有一丢丢的 nodejs
koa
基础. 主要用于搭建一个 web
服务器, 固然没有基础也没啥关系, 先去 node koa 官网看看. 回不回来???前端
随你咯 😄java
继续上一步, 本文只会建立一个后端项目. 因此不须要在 ./fe
目录下建立前端项目啦, 项目目录以下.node
其中, serverProxy
目录是项目的主目录. www
目录即为前端静态文件的托管目录. base.js
为后端主程序, add.js subtract.js
分别表示两个第三方服务, 分别提供了计算加法和减法的能力.ios
cd be/serverProxy
将路径切换到 serverProxy
npm init -y
初始化为一个 node 项目npm i koa -S
完成 koa
的安装验证 koa
安装是否完成git
base.js
写入如下内容const Koa = require('koa');
const app = new Koa();
const PORT = 1234;
app.use((ctx) => {
ctx.body = 'Hello World';
});
app.listen(PORT, () => {
console.log('the server is listen: ', PORT);
});
复制代码
node base.js
看到命令行中输出了 the server is listen: 1234
说明启动成功此时代码github
koa-static
模块在以前文章中, 咱们老是要经过 live-server
启动一个本地的静态资源服务. 用于托管前端静态文件. koa
生态中有现成的中间件koa-static
能够提供直接在后端项目中建立静态资源服务的能力.web
npm i koa-static -S
安装 koa-static
base.js
const Koa = require('koa');
// 引入 koa-static
const koaStatic = require('koa-static');
const app = new Koa();
const PORT = 1234;
// 使用 koa-static 中间件, 并指定静态文件目录为 www
app.use(koaStatic('./www'));
app.use((ctx) => {
console.log(ctx.req.url);
ctx.body = 'Hello World';
});
app.listen(PORT, () => {
console.log('the server is listen: ', PORT);
});
复制代码
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ServerProxy 实现跨域</title>
</head>
<body>
ServerProxy 实现跨域
</body>
</html>
复制代码
node base.js
重启项目以前准备的 html 页面赫然在目 😄. 至此, 静态文件服务就搭建成功了(至关于咱们本身实现了一个 live-server
)代码地址面试
经过 koa-static
中间件, 咱们搭建了一个本身的静态文件服务器. 接下来演示一个不跨域的请求...
const Koa = require('koa');
const koaStatic = require('koa-static');
const app = new Koa();
const PORT = 1234;
app.use(koaStatic('./www'));
app.use((ctx) => {
let ret;
// 获取本次接收的请求的请求路径
const path = ctx.req.url;
// 若是请求路径以api开头, 那么做为接口请求处理
if (path.startsWith('/api')) {
// 这样实现的路由不是很优雅, 可是能用 😂
switch (path) {
case '/api/getFriend':
ret = { name: 'quanquan', friend: 'gl' };
break;
default:
ret = { errno: 1, errmsg: '未知接口' };
break;
}
}
ctx.body = ret;
});
app.listen(PORT, () => {
console.log('the server is listen: ', PORT);
});
复制代码
上述代码中定义了 /api/getFriend
接口, 经过浏览器访问的以下图:
node base.js
重启后端项目
接下来修改前端代码. 经过 ajax 的方式访问该接口
修改前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ServerProxy 实现跨域</title>
</head>
<body>
ServerProxy 实现跨域
<script> // 一个常规的 ajax, 感兴趣的兄弟们也看看. 手写 ajax 好多面试官还在考 var xhr = new XMLHttpRequest() xhr.open('GET', '/api/getFriend') xhr.onreadystatechange = function() { if(xhr.readyState === 4 && xhr.status === 200) { console.log('接口返回的数据为: ', xhr.responseText) } } xhr.send() </script>
</body>
</html>
复制代码
刷新浏览器, 控制台展现以下. 没有报错, 返回的信息前端直接拿到了.
原来先后端同域时数据交互这么的简单.
先后端跑通阶段代码
项目开发中常常会用到一些基础服务, 好比天气信息, 地理位置信息等等. 这些服务能力通常是经过调用第三方的接口来实现的(你开发一个网站, 先发射一颗气象卫星到天上也不太现实). 这一步咱们建立两个第三方服务, 分别提供加法和减法运算.
加法运算服务 add.js
const Koa = require('koa');
const app = new Koa();
const PORT = 1111;
app.use((ctx) => {
// 获取参数
const { a, b } = ctx.query;
// 尝试将参数转化为数字后进行加法操做
const result = Number(a) + Number(b);
ctx.body = { result };
});
app.listen(PORT, () => {
console.log('the server is listen: ', PORT);
});
复制代码
执行命令 node add.js
启动程序, 而后浏览器端访问localhost获得的结果以下, 说明加法计算服务启动成功.
减法运算服务 subtract.js
const Koa = require('koa');
const app = new Koa();
const PORT = 2222;
app.use((ctx) => {
// 获取参数
const { a, b } = ctx.query;
// 尝试将参数转化为数字后进行减法操做
const result = Number(a) - Number(b);
ctx.body = { result };
});
app.listen(PORT, () => {
console.log('the server is listen: ', PORT);
});
复制代码
执行命令 node subtract.js
启动程序, 而后浏览器端访问localhost获得的结果以下, 说明减法计算服务启动成功.
目前代码
建立完加法和减法服务, 咱们仍是有侥幸心理忍不住在前端项目里访问一下试试, 万一能通了呢? 就不用费事儿研究跨域了, 尝试一下
修改前端代码中的接口地址 xhr.open('GET', 'http://localhost:1111/?a=1&b=2')
完整代码, 以后直接刷新浏览器(请思考, 为何修改了 js 文件须要执行 node ...
重启服务, 而修改了 html 文件只须要刷新浏览器就能够了呢?).
回想一下以前的思路. 浏览器有同源策略的限制服务器没有. 咱们的前端项目托管在后端项目中因此访问咱们本身的后端不跨域. 咱们的后端请求第三方服务没有限制. 那么 ^_^
npm i axios -S
安装 axios, 后端经过它来请求目标服务器base.js
const Koa = require('koa');
const koaStatic = require('koa-static');
const axios = require('axios');
const app = new Koa();
const PORT = 1234;
app.use(koaStatic('./www'));
app.use(async (ctx) => {
let ret;
// 获取本次接收的请求的请求路径
const path = ctx.req.url.split('?')[0];
console.log('ctx.query.server', ctx.query.server);
// 若是请求路径以api开头, 那么做为接口请求处理
if (path.startsWith('/api')) {
// 这样实现的路由不是很优雅, 可是能用 😂
switch (path) {
case '/api/getFriend':
ret = { name: 'quanquan', friend: 'gl' };
break;
// 若是接口须要代理接口路径为 /api/proxy
case '/api/proxy':
// axios 直接访问前端给出的目标服务器url, 并将目标服务器返回的数据直接返回给前端
ret = (await axios.get(ctx.query.server)).data;
break;
default:
ret = { errno: 1, errmsg: '未知接口' };
break;
}
}
ctx.body = ret;
});
app.listen(PORT, () => {
console.log('the server is listen: ', PORT);
});
复制代码
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ServerProxy 实现跨域</title>
</head>
<body>
<h3>ServerProxy 实现跨域</h3>
a: <input type="text" id="a" value="1">
b: <input type="text" id="b" value="2">
<button id="add">计算加法</button>
<button id="subtrnct">计算减法</button>
<div>计算结果为: <span id="ret"></span></div>
<script> var aDom = document.getElementById('a') var bDom = document.getElementById('b') var addBtn = document.getElementById('add') var subtrnctDom = document.getElementById('subtrnct') var retDom = document.getElementById('ret') function add() { if(!a.value.trim() || !b.value.trim()) return var xhr = new XMLHttpRequest() xhr.open('GET', '/api/proxy' + '?server=' + encodeURIComponent('http://localhost:1111/?a='+ a.value +'&b=' + b.value)) xhr.onreadystatechange = function() { if(xhr.readyState === 4 && xhr.status === 200) { console.log('接口返回的数据为: ', xhr.responseText) retDom.innerHTML = JSON.parse(xhr.responseText).result } } xhr.send() } function subtrnct() { if(!a.value.trim() || !b.value.trim()) return var xhr = new XMLHttpRequest() xhr.open('GET', '/api/proxy' + '?server=' + encodeURIComponent('http://localhost:2222/?a='+ a.value +'&b=' + b.value)) xhr.onreadystatechange = function() { if(xhr.readyState === 4 && xhr.status === 200) { console.log('接口返回的数据为: ', xhr.responseText) retDom.innerHTML = JSON.parse(xhr.responseText).result } } xhr.send() } addBtn.addEventListener('click', add) subtrnctDom.addEventListener('click', subtrnct) </script>
</body>
</html>
复制代码
最后结果:
结语: ServerProxy 的原理大概就是这个样子的啦, 经过 ajax 访问同域后端服务, 后端服务访问目标服务并将目标服务返回的内容透传给前端. 固然实际操做起来不会像例子这么简单. 个人另外一个系列文章【手把手带你撸一个接口测试工具】将会详细介绍复杂一些的状况, 包括不一样的请求类型, 请求头设置以及响应头获取等等. 但愿感兴趣的小伙伴继续关注.
关于跨域的其余方式: document.domain 一行代码能够搞定, 适合同主域名不一样子域名的状况. postMessage 须要添加额外 iframe, 总体实现较为简单, 一个 API 搞定, 感兴趣的同窗能够看看文档. 还有一些比较小众的作法 flash
CSST
前端打点尝尝用到的 img 标签等等, 这里就不一一列举了, 学无止境...