1、需求
前後端分離項目中,需要使用反向代理,已解決跨域訪問問題,在這裏需要了解正向代理、反向代理等概念,
2、正向代理
正向代理比較好理解,類似於一個跳板,用來代替客戶訪問外部資源。
例如:
A向C借錢,由於一些情況不能直接向C借錢,於是A想了一個辦法,他讓B去向C借錢,這樣B就代替A向C借錢,A就得到了C的錢,C並不知道A的存在,B就充當了A的代理人的角色。
總的來說:
正向代理(forward proxy) ,一個位於客戶端和原始服務器之間的服務器,爲了從原始服務器取得內容,客戶端向代理髮送一個請求並制定目標(原始服務器),然後代理向原始服務器轉發請求並將獲得的內容返回給客戶端,客戶端才能使用正向代理。
3、反向代理
反向代理(Reverse Proxy),以代理服務器來接受internet上的連接請求,然後將請求轉發給內部網絡上的服務器,並將從服務器上得到的結果返回給internet上請求的客戶端,此時代理服務器對外表現爲一個反向代理服務器。
理解起來有些抽象,可以這麼說:A向B借錢,B沒有拿自己的錢,而是悄悄地向C借錢,拿到錢之後再交給A,A以爲是B的錢,他並不知道C的存在。
前端web服務器選擇express,使用 http-proxy 實現反向代理:
const _ = require('lodash');
const path = require('path');
const httpProxy = require('http-proxy');
const request = require('request');
const chokidar = require('chokidar');
const proxyRespJson = require('node-http-proxy-json');const proxy = httpProxy.createProxyServer();
var mockData = {};
const mockDataPath = path.resolve(__dirname, '../mockData');// 監聽mock文件變化,重新加載mock數據
chokidar.watch(mockDataPath).on('change', (filepath) => {
console.info('mock數據變化,重新加載', filepath);
loadMockData();
});// 加載mock數據
function loadMockData() {
try {
// require.cache對象, 代表緩存了所有已被加載模塊的緩存區
Object.keys(require.cache).forEach((cachePath) => {
if (cachePath.startsWith(mockDataPath)) {
delete require.cache[cachePath];
}
});
mockData = require(mockDataPath);
} catch (error) {
console.error('加載mock數據異常', error);
}
}proxy.on('error', (err, req, res) => {
console.error('代理失敗:', err);
res.writeHead(200, {
'Content-Type': 'text/plain;charset=UTF8'
});res.send('抱歉,代理服務器異常...');
});proxy.on('proxyReq', (proxyReq) => {
// HTTP請求由三部分組成,分別是:請求行,消息報頭,請求正文。
// http auth是一種基礎的用戶驗證,原理是將用戶名:密碼base64加密後放在http的請求頭部Authorization 發給服務器
// restful api 的一個特點即無狀態,每次對敏感資源的訪問都需要進行登陸驗證,可以用http auth來很好的開發restful api。// proxyReq.setHeader('Authorization', authorizationValue);
});proxy.on('proxyRes', (proxyRes, req, res) => {
console.log(`請求代理狀態: ${proxyRes.statusCode} <==> path: ${req.path}`);
const useMockStatusCode = [200, 404, 403, 500];if (useMockStatusCode.indexOf(proxyRes.statusCode) !== -1) {
// 獲取 writHead 方法,發送一個響應頭給請求,這裏獲取此方法,改變響應頭狀態嘛
/* 參數列表:
* .writeHead(200, {
'Content-Length': Buffer.byteLength(body),
'Content-Type': 'text/plain'
})
狀態碼:如 404
headers 是響應頭
* */
const _writeHead = res.writeHead
// writHead() 在消息中只能被調用一次,且必須在res.end()之前調用
// response.setHeader() 設置的響應頭會與 response.writeHead() 設置的響應頭合併,且 response.writeHead() 的優先Object.assign(res, {
writeHead: () => {
// 函數 apply() 方法,執行this爲res,傳遞參數 200,
_writeHead.apply(res, [200, proxyRes.headers])
}
})proxyRespJson(res, proxyRes.headers['content-encoding'], (body) => {
console.info('請求遠端服務異常,採用本地mock數據', req.path)
const callback = mockData[req.path]
// 傳入req,用於部分mock獲取request的數據
return callback ? callback(req) : {}
})
}
});module.exports = (app) => {
/*
* app.all()函數可以匹配所有的HTTP動詞,也就是說它可以過濾所有路徑的請求
* app.all(path,function(request, response))
* */
app.all("/api/v1/*", (req, res) => {
proxy.web(req, res,
{
target: "https://cnodejs.org",
changeOrigin: true, // 是否更改原始的host頭字段爲target url,默認爲 false, 表示用於更改目標地址頭信息
proxyTimeout: 10 * 60 * 1000
});
});
};// https://cnodejs.org/api/v1/topics
訪問結果:
參考文章:
https://blog.csdn.net/zt15732625878/article/details/78941268