前端小結:反向代理 - http-proxy

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的存在。

4、node插件http-proxy實現反向代理

   前端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

    https://www.cnblogs.com/Anker/p/6056540.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章