前端小结:反向代理 - 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

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