关于webpack-dev-server代理的哪些事儿

@GuoYongfeng 2018-04-19 06:42:27发表于 iuap-design/blog

0.起因

浅笑同学根据 webpack 官方提供的文档想要实现服务代理的时候,发现代理不成功。

这里先链接到官方文档:https://webpack.js.org/configuration/dev-server/#devserver-proxy

我们的代码是这样的:(https://mock.yonyoucloud.com 是我们基于开源搭建的mock数据模拟平台)

module.exports = {
...
devServer: {
    proxy: {
  "/api": {
    target: "https://mock.yonyoucloud.com/1643/mock",
    pathRewrite: {"^/api" : ""}
  }
}
}
...
}

1.分析

  1. 是不是 target 写的不对?遂改成:target: "https://mock.yonyoucloud.com
  2. 是不是不支持 https?看官网文档,也是支持的,只要设置 secure 就行。
proxy: {
  "/api": {
    target: "https://other-server.example.com",
    secure: false
  }
}

最终无果。

2.查看源码

webpack-dev-server 实现 proxy 的功能是依赖的 http-proxy-middleware ,代码:

const httpProxyMiddleware = require('http-proxy-middleware');

...

const getProxyMiddleware = (proxyConfig) => {
          const context = proxyConfig.context || proxyConfig.path;

          // It is possible to use the `bypass` method without a `target`.
          // However, the proxy middleware has no use in this case, and will fail to instantiate.
          if (proxyConfig.target) {
            return httpProxyMiddleware(context, proxyConfig);
          }
        };

SO,我们一路追到了:https://github.com/chimurai/http-proxy-middleware

var express = require('express');
var proxy = require('http-proxy-middleware');

var app = express();

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

// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar

所以,你以后要是想自己实现一个代理封装,也可以基于:http-proxy-middleware。

3.深入

从上面的示例代码中,首先引入眼帘的就是:changeOrigin: true,比较奇怪,因为webpack-dev-server 的文档上面没有这个参数。返回回去看文档,是这样写的:

Proxying some URLs can be useful when you have a separate API backend development server and you want to send API requests on the same domain.

The dev-server makes use of the powerful http-proxy-middleware package. Checkout its documentation for more advanced usages.

意思就是,我这个文档里面的参数也没写全,要查看更多配置,你去看 http-proxy-middleware 的文档好了。但是 http-proxy-middleware 上面的文档也没有更多参数。

那就进一步看一下他的实现源码,看到是基于 http-proxy 实现的,所以继续查看 http-proxy 的源码。

4.关于 http-proxy

http-proxy 的源码仓库在https://github.com/nodejitsu/node-http-proxy,在这里看到了非常全的配置项:

target: url string to be parsed with the url module
forward: url string to be parsed with the url module
agent: object to be passed to http(s).request (see Node's https agent and http agent objects)
ssl: object to be passed to https.createServer()
ws: true/false, if you want to proxy websockets
xfwd: true/false, adds x-forward headers
secure: true/false, if you want to verify the SSL Certs
toProxy: true/false, passes the absolute URL as the path (useful for proxying to proxies)
prependPath: true/false, Default: true - specify whether you want to prepend the target's path to the proxy path
ignorePath: true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request (note: you will have to append / manually if required).
localAddress: Local interface string to bind for outgoing connections
changeOrigin: true/false, Default: false - changes the origin of the host header to the target URL

其中最下面就说明 changeOrigin 是用来干啥的,他的实现代码也可以找到:

  outgoingPath = !options.ignorePath ? outgoingPath : '';

  outgoing.path = common.urlJoin(targetPath, outgoingPath);

  if (options.changeOrigin) {
    outgoing.headers.host =
      required(outgoing.port, options[forward || 'target'].protocol) && !hasPort(outgoing.host)
        ? outgoing.host + ':' + outgoing.port
        : outgoing.host;
  }
  return outgoing;

5.方案和总结

前面来来回回的查找,也只是追溯人家的实现方式,方便我们理解,以及自己遇到这种需求的时候也可以直接开撸。好了,对于我们,要解决的问题,只是代理的实现,可以这样配置:

proxy: {
  "/api": {
    target: "https://mock.yonyoucloud.com",
    secure: false,
    changeOrigin: true
  }
}

结语:还有很多功能没用到,可以自己继续探索,如果遇到了坑,不要慌,一路追到底。