简介
例如:我们当前主机为http://localhost:3000/,现在我们有一个需求,如果我们请求/api,我们不希望由3000来处理这个请求,而希望由另一台服务器来处理这个请求怎么办?我们可以通过http-proxy-middleware将请求代理转发到其他服务器的中间件。
安装
npm install --save-dev http-proxy-middleware
核心概念
proxy([context,] config)
var proxy = require('http-proxy-middleware');
var apiProxy = proxy('/api', { target: 'http://www.example.org' });
// \____/ \_____________________________/
// | |
// context options
// 'apiProxy' is now ready to be used as middleware in a server.
- context: 确定应将哪些请求代理到目标主机
- options.target: target 目标服务器
匹配规则
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
-
路径匹配
proxy({...})- 匹配任何路径,所有请求都会被代理proxy('/', {...})- 匹配任何路径,所有请求都会被代理proxy('/api', {...})- 匹配以/api开头的路径
-
多路径匹配
proxy(['/api', '/ajax', '/someotherpath'], {...})
-
通配符路径匹配
-
proxy('**', {...})匹配任何路径,所有请求都会被代理 -
proxy('**/*.html', {...})匹配任何以.html结尾的路径 -
proxy('/*.html', {...})直接在绝对路径下匹配路径 -
proxy('/api/**/*.html', {...})匹配/api路径中以.html结尾的请求 -
proxy(['/api/**', '/ajax/**'], {...})结合多种模式 -
proxy(['/api/**', '!**/bad.json'], {...})排除
Note: 在多路径匹配中,不能同时使用字符串路径和通配符路径。
-
-
自定义匹配
为了实现完全控制,您可以提供自定义功能来确定应否代理哪些请求。
/**
* @return {Boolean}
*/
var filter = function(pathname, req) {
return pathname.match('^/api') && req.method === 'GET';
};
var apiProxy = proxy(filter, { target: 'http://www.example.org' });
配置项
http-proxy-middleware options
-
option.pathRewrite: object/function, 重写目标网址路径。对象键将用作
RegEx来匹配路径// rewrite path pathRewrite: {'^/old/api' : '/new/api'} // remove path pathRewrite: {'^/remove/api' : ''} // add base path pathRewrite: {'^/' : '/basepath/'} // custom rewriting pathRewrite: function (path, req) { return path.replace('/api', '/base/api') } -
option.router: object/function,重新定位
option.target以用于特定请求// Use `host` and/or `path` to match requests. First match will be used. // The order of the configuration matters. router: { 'integration.localhost:3000' : 'http://localhost:8001', // host only 'staging.localhost:3000' : 'http://localhost:8002', // host only 'localhost:3000/api' : 'http://localhost:8003', // host + path '/rest' : 'http://localhost:8004' // path only } // Custom router function router: function(req) { return 'http://localhost:8004'; } -
option.logLevel: string, ['debug', 'info', 'warn', 'error', 'silent']. Default:
'info' -
option.logProvider: function, 修改或替换日志. Default:
console.// simple replace function logProvider(provider) { // replace the default console log provider. return require('winston'); }// verbose replacement function logProvider(provider) { var logger = new (require('winston')).Logger(); var myCustomProvider = { log: logger.log, debug: logger.debug, info: logger.info, warn: logger.warn, error: logger.error }; return myCustomProvider; }
http-proxy events
-
option.onError: function, 订阅
http-proxy的错误事件以进行自定义错误处理function onError(err, req, res) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end( 'Something went wrong. And we are reporting a custom error message.' ); } -
option.onProxyRes: function, 订阅
http-proxy的proxyRes事件function onProxyRes(proxyRes, req, res) { proxyRes.headers['x-added'] = 'foobar'; // add new header to response delete proxyRes.headers['x-removed']; // remove header from response } -
option.onProxyReq: function, 订阅
http-proxy的proxyReq事件function onProxyReq(proxyReq, req, res) { // add custom header to request proxyReq.setHeader('x-added', 'foobar'); // or log the req } -
option.onProxyReqWs: function, 订阅
http-proxy的proxyReqWs事件function onProxyReqWs(proxyReq, req, socket, options, head) { // add custom header proxyReq.setHeader('X-Special-Proxy-Header', 'foobar'); } -
option.onOpen: function, 订阅
http-proxy的open事件function onOpen(proxySocket) { // listen for messages coming FROM the target here proxySocket.on('data', hybiParseAndLogMessage); } -
option.onClose: function, 订阅
http-proxy的close事件function onClose(res, socket, head) { // view disconnected websocket connections console.log('Client disconnected'); }
http-proxy options
基础的http-proxy库提供以下选项。 http-proxy library.
在egg项目中应用
因为http-proxy-middleware不是标准的koa中间件,需要使用koa2-connect包装一下
// app/middleware/http_proxy.ts
import proxy from 'http-proxy-middleware';
import k2c from 'koa2-connect';
import pathToRegexp from 'path-to-regexp';
export default (options) => {
// test1代理
const test1Proxy = k2c(proxy({
target: 'https://test1.com.cn',
changeOrigin: true,
pathRewrite: {
'/proxy/test': ''
},
onProxyReq(proxyReq, req, res) {
proxyReq.setHeader('cookie', req.headers.cookie);
}
}));
const test2Proxy = .....
return async function httpProxy(ctx, next) {
if (pathToRegexp('/proxy/test/(.*)').exec(ctx.request.url)) {
test1Proxy(ctx, next);
}
if(...){
test2Proxy(ctx, next)
}
await next();
};
};
当然需要在 bodyparser 之前引入
// app.ts
import { Application } from 'egg';
export default (app: Application) => {
app.config.coreMiddleware.unshift('httpProxy');
};