Vue 本地开发请求跨域的解决以及服务器代理的原理,永远不要做只用不学的人【三】

2,113 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文同时参与 「掘力星计划」  ,赢取创作大礼包,挑战创作激励金

前言

上一章我们学习了node-http-proxy的源码以及原理、基础的使用方法,我们也讲过:讲http-proxy-middleware之前我们要先讲一下node-http-proxy。这又是为什么了?不急,这篇就告诉你答案

感兴趣的人可以前往查看历史文章。

  1. Vue 本地开发请求跨域的解决以及服务器代理的原理,永远不要做只用不学的人【一】
  2. Vue 本地开发请求跨域的解决以及服务器代理的原理,永远不要做只用不学的人【二】

一、http-proxy-middleware 与 node-http-proxy的关系

  1. 配置http-proxy-middleware可以很容易地在connect, express, browser-sync实现http代理
  2. http-proxy-middleware 是基于node-http-proxy实现的中间件

二、http-proxy-middleware简单用法

- /api 请求代理到http://xxx.com的例子
var express = require('express');
var proxy = require('http-proxy-middleware');

var app = express();

app.use('/api', proxy({target: 'http://xxx.com', changeOrigin: true}));
app.listen(3000);

三、使用node-http-proxy进行http代理的代码对比

var express = require('express')
var httpProxy = require('http-proxy')


var options = {target: "http://xxx.com/users",changeOrigin: true};
var httpProxyServer = httpProxy.createProxyServer(options);
function jsonHttpProxy(req, res) {
    httpProxyServer.web(req, res);
}
var app = express()
app.use('/users', jsonHttpProxy)
app.listen(3002)
  • 由上述代码可以看出,当要访问/users时,需要target中对应代理网站中也设置对应的/users路径
  • 而在http-proxy-middleware中有一个req.url的设置,可以根据当前访问路径自动映射代理网站相应路径
  • httpProxyServer.web()表示代理http或者https常规的代理连接,而想要代理websocket的连接则需要使用httpProxyServer.ws()方法

四、探究http-proxy-middleware源码处理过程

4.1. http-proxy-middleware源码目录

image.png

logger.js和errors.js分别用于输出日志和错误信息这里不作分析

4.2 http-proxy-middleware代理普通请求(http和https)
  • 在indexjs中能看到主入口文件,下面这段省略的源码能充分反映普通请求代理中http-proxy-middleware和node-http-proxy的关系
var httpProxy = require('http-proxy')

function HttpProxyMiddleware (context, opts) {
    ……省略
    return middleware
    
     var proxy = httpProxy.createProxyServer({})
     
     function middleware (req, res, next) {
      ……省略
      var activeProxyOptions = prepareProxyRequest(req)
      proxy.web(req, res, activeProxyOptions)
      ……省略
    
     }
}
  • 与三中的代码比较最主要的区别是对options做了处理,也就是activeProxyOptions = prepareProxyRequest(req),下面分析prepareProxyRequest函数做的处理
  • 以访问options = {target: "xxx.com", changeOrigin: true}为例,访问http://localhost:3000/users
function prepareProxyRequest (req) {
    //这一步的处理非常关键,可以将请求路径对应上代理的路径
    //如果不设置这一步,那么代理的路径就是对应了target不会变化
    req.url = (req.originalUrl || req.url)

    // proxyOptions就是原始的options
    var originalPath = req.url
    var newProxyOptions = _.assign({}, proxyOptions)

    //这里是处理更多变得路径代理情况,下面详细介绍
    __applyRouter(req, newProxyOptions)
    __applyPathRewrite(req, pathRewriter)

    ……省略logger部分代码

    return newProxyOptions
  }
  1. 从http-proxy-middleware文档中可以看到关于router和pathRewriter的举例用法,处理过程就是对应上面的__applyRouter和__applyRewriter方法

  2. __applyRouter的处理在router.js中,主要处理过程是遍历options.router,然后和请求的host或者host+path比对是否相符,然后将相对应的值赋予target;如果router是函数则传入req值,执行函数得到返回值

  3. __applyRewriter的处理在path-rewriter.js中,主要处理过程是遍历options.pathRewrite,用正则包装key,对应value,返回一个检测并改变req.url的函数,以此对应相应的代理网站的路径

pathRewrite: {
    '^/api/old-path' : '/api/new-path',     // rewrite path
    '^/api/remove/path' : '/path'           // remove base path
    },
router: {
    // when request.headers.host == 'dev.localhost:3000',
    // override target 'http://www.example.org' to 'http://localhost:8000'
    'dev.localhost:3000' : 'http://localhost:8000'
}

  • config-factory.js用于处理传入的context和options参数
  • 例如:处理proxy(['/api', '/ajax', '/someotherpath'], {...})的context

  • context-matcher.js用于检测当proxy中传入了context参数时与请求路径的比对
  • 例如: proxy('/api', {...}) - 只能代理请求以/api开头的请求
4.3 http-proxy-middleware代理websocket请求
  • ws代理的核心处理代码
    // 这里的debounced起到了节流函数的作用,防止短时间内多次调用处理函数
    var wsUpgradeDebounced = _.debounce(handleUpgrade)
     if (proxyOptions.ws === true) {
      catchUpgradeRequest(req.connection.server)
    }
    
    //监听upgrade
    function catchUpgradeRequest (server) {
        if (!wsInitialized) {
          server.on('upgrade', wsUpgradeDebounced)
          wsInitialized = true
        }
      }
    
    //真正调用node-http-proxy的ws方法的处理函数
    function handleUpgrade (req, socket, head) {
        wsInitialized = true
    
        if (shouldProxy(config.context, req)) {
          var activeProxyOptions = prepareProxyRequest(req)
          proxy.ws(req, socket, head, activeProxyOptions)
        }
  }

我们的Vue 本地开发请求跨域的解决以及服务器代理的原理系列就讲到这里了,感谢大家的观看。