系列文章: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 標籤等等, 這裏就不一一列舉了, 學無止境...