api代理模块,node server端代理请求后端service用。
- 确定honeycomb项目的启动端口和prefix,比如: 项目的启动端口为8001,prefix为 'example'
- 整理远程调用的服务,给每个服务起个英文名称,比如: 上面的两个服务 视频服务(video) 音乐服务(music) 聊天服务(chat)
- 配置在router.js中,进行如下配置
- 请注意,为了确保安全,所有api只支持白名单
// router.js
const app = require('./app');
const Proxy = require('hc-proxy');
const proxyInstance = new Proxy({
service: {
video: {
endpoint: 'http://localhost:7001/',
client: 'http',
api: [
'/api/aaa',
'/api/c/d'
]
},
music: {
endpoint: 'http://192.168.1.1:7001/',
client: 'http',
api: [
'/api/bbb'
]
}
},
headers: [
'x-csrf-token',
'X-Operator'
]
});
// 代理的接口前缀 /api/proxy, 可以自定义
proxyInstance.setProxyPrefix('/api/proxy');
module.exports = function (router) {
proxyInstance.mount(router, app);
};
访问地址为:
${服务地址:服务端口/prefix} + ${proxyPrefix} + ${被代理服务名} + ${具体api}
上面的配置案例里:
1. 视频服务: curl http://localhost:8001/example/api/proxy/video/api/aaa => http://localhost:7001/api/aaa
2. 视频服务: curl http://localhost:8001/example/api/proxy/video/api/c/somewhat?xxx=123 => http://localhost:7001/api/c/somewhat?xxx=123
3. 音乐服务: curl http://localhost:8001/example/api/proxy/music/api/bbb => http://192.168.1.1:7001/api/bbb
// router.js
const app = require('./app');
const Proxy = require('hc-proxy');
const proxyInstance = new Proxy({
service: {
music: {
endpoint: 'http://192.168.1.1:7001/',
client: 'http',
api: [
'/api/bbb'
]
},
chat: {
endpoint: 'http://localhost:7001/',
client: 'websocket',
api: [
'/ws/a'
]
}
},
headers: [
'x-csrf-token',
'X-Operator'
]
});
proxyInstance.setProxyPrefix('/api/proxy');
module.exports = function (router) {
proxyInstance.mount(router, app);
};
访问地址为 ${honeycomb服务地址:honeycomb服务端口/honeycomb的prefix} + ${proxyPrefix} + ${被代理服务名} + ${具体api}
1. 音乐服务: curl http://localhost:8001/example/api/proxy/music/api/bbb => http://192.168.1.1:7001/api/bbb
2. 聊天服务: curl http://localhost:8001/example/api/proxy/chat/ws/somewhat => http://localhost:7001/ws/somewhat // 支持websocket
// router.js
const app = require('./app');
const Proxy = require('hc-proxy');
const proxyInstance = new Proxy({
service: {
music: {
endpoint: 'http://192.168.1.1:7001/',
client: 'http',
api: [
{
path: '/api/404',
return: 404
},
{
path: '/api/upload',
file: true
},
{
path: '/api/upload_limited',
file: {
maxFileSize: 100 // 100B
},
beforeRequest: (req, options, config) => {},
beforeResponse: (req, res, data) => {}
}
]
}
},
headers: [
'x-csrf-token',
'X-Operator'
]
});
proxyInstance.setProxyPrefix('/api/proxy');
module.exports = function (router) {
proxyInstance.mount(router, app);
};
1. 上传文件1: curl http://localhost:8001/example/api/proxy/music/api/upload => http://192.168.1.1:7001/api/upload
2. 上传文件2: curl http://localhost:8001/example/api/proxy/music/api/upload_limited => http://192.168.1.1:7001/api/upload_limited
约定:
- proxy挂在的http服务称为 "代理服务"
- 被proxy代理的远端服务称为 "远端服务"
options.service 详情
{
${serviceCode}: {
/* 每个远端服务的服务地址,如: 'http://localhost:7001' */
endpoint: ${endpoint},
accessKeyId: ${accessKeyId},
accessKeySecret: ${accessKeySecret},
/* 同 hc-service-client 配置,见文档: https://www.npmjs.com/package/hc-service-client */
headerExtension: ${headerExtension},
/* 选填,透传的header列表,同 hc-service-client 配置,见文档: https://www.npmjs.com/package/hc-service-client */
headers: {Array},
/* 可选,发起请求的agent,目前只支持'appClient' / 'http' / 'websocket' / 'serviceWebsocket',默认为'appClient',其中 appClient 和 serviceWebsocket 带了honeycomb体系中的签名逻辑 */
client: ${client},
/* 接口超时时间,单位毫秒 */
timeout: ${timeout},
/* 可选,delete方法使用querystring代理, 默认为true */
useQuerystringInDelete: ${useQuerystringInDelete}, // 只有 appClient / urllib 模式有效
/* 可选,用户覆盖的urllibOption,覆盖系统默认值,优先级: service.api.urllibOption > service.urllibOption > hc-proxy默认设置 */
urllibOption: {Object}, // 只有 appClient / urllib 模式有效
/* 覆盖转发时的5XX的errorCode */
defaultErrorCode: {Number}
/* 排除列表, 不代理的接口 */
exclude: {Array}
/* 路由前缀 */
routePrefix: {String}
/* 是否开启路径支持正则匹配, 默认关闭,开启请确保安全 */
enablePathWithMatch: {Boolean} false
api: [
/* 接口配置可以是简单的一个string */
'${ApiPathString}',
{
/* api访问的path */
path: {String}
/* 如若定义,会覆盖proxyPrefix, 给特殊场景定义接口路径用 */
route: {String}
/* 接口方法 */
method: 'GET|POST|PUT|DELETE|PATCH'
/* 接口超时时间, 单位毫秒,覆盖上面配置的服务的通用超时,通常用来设置特殊接口的超时时长 */
timeout: {Number},
/* 是否透传, 开启透传之后,body不落地,直接pipe到远端; 开启pipe之后,body内容不参与签名(签名里的body='') */
pipe: true,
/* 请求的默认querystring信息, 用于配置默认的query参数(代理请求时自动加上) */
defaultQuery: {Object|String},
/**
* 发起请求前的hook, beforeRequest(req, apiReq, config)
* @param req {Request} 客户端请求对象request,
* @param options {Object} urlib的配置信息,
* @param config {Object} api的配置信息
*/
beforeRequest: {Function(req, options, config)},
/**
* 请求从服务接口返回之后的hook,afterResponse(req, res, apiRes)
* @param req {Request} 客户端请求的request对象,
* @param res {Response} proxy端请求的response对象,
* @param data {Response} 返回数据
* @return data
*/
beforeResponse: {Function(req, res, data)},
/* delete方法使用querystring代理, 默认为true */
useQuerystringInDelete: {Boolean},
/** 用户覆盖的urllibOption,覆盖系统默认值,优先级: service.api.urllibOption > service.urllibOption > hc-proxy默认设置 */
urllibOption: {Object}
}
]
}
}
通用配置:
- headers: 选填,同 hc-service-client 配置,见文档: https://www.npmjs.com/package/hc-service-client
- accessKeyId: 选填,同 hc-service-client 配置,默认 'hc-proxy',详见 https://www.npmjs.com/package/hc-service-client
- accessKeySecret: 同 hc-service-client 配置,必填,不填时取 app.config.systemToken,详见 https://www.npmjs.com/package/hc-service-client
- headerExtension: 同 hc-service-client 配置,见文档: https://www.npmjs.com/package/hc-service-client
options.headers 用于声明proxy需要转发的header。 默认情况下,proxy不转发客户端过来的header,只有在proxyHeaders中配置的header才会被转发。
- proxyPrefix
setProxyPrefix方法用于指定 hc-proxy 挂载在代理服务上的总前缀
如: 默认 proxyPrefix = /api/proxy 则: 所有请求远端服务的请求,格式为 ${localHttpServer}/api/proxy/${serviceCode}/${remoteApi}
将proxy的配置挂在到代理服务。
- router: Express Router instance
- app: app.server 是一个 http.Server 的实例,honeycomb中,直接 require('./app') 能获得
'use strict';
const app = require('./app');
const Proxy = require('hc-proxy');
const proxyInstance = new Proxy({
service: {
otm: {
endpoint: 'http://dev.dtboost.biz.aliyun.test/otm_v2', // 自动截取最后的'/'
client: 'appClient', // 默认appClient
timeout: 10000, // 默认60000
api: [
'/api/a', // 支持 * 代理某个path下的所有api
'/otm_v2/api/entities/list', // 代理这个 url 的 GET POST PUT DELETE 方法
{path: '/otm_v2/api/entities/list', method: 'GET'}, // 只代理 GET 方法
{path: '/otm_v2/api/entities/list', client: 'appClient'}, // 显式指定 method
{path: '/otm_v2/api/schemas', method: ['GET', 'POST']}, // 只支持 GET POST 方法
{host: 'http://taobao.com/api', path: '/tag_factory_v2/api/:id', method: 'GET'}, // 不同域时,指定host
{path: 'http://taobao.com/api', route: '/api/proxy/taobao_api'}, // 指定route的代理,不指定route时,所有代理接口请求 /<-app_name->/api/proxy 获得代理
'/otm_v2/api/tags/query_tags_entity_pagenum', //
{path: '/otm_v2/api/schemas/:id', route: '/api/proxy/schemas/:id'} // 带参数的url,保证route和url中的param一致,也可以不填route
]
},
websocket: {
endpoint: 'http://dev.dtboost.biz.aliyun.test/websocket', // 远程的websocket地址
client: 'websocket', // 选择websocket作为连接的client
api: [
'/ws' // 配置连接路径,不在的路径返回404
]
}
},
headers: [
'x-csrf-token',
'X-Operator'
]
});
proxyInstance.setProxyPrefix('/api/proxy');
module.exports = function (router) {
proxyInstance.mount(router, app);
};
在命令行启动命令前加入DEBUG=hc-proxy
// 以启动命令为 honeycomb start 为例
DEBUG=hc-proxy honeycomb start
// ...
// hc-proxy [GET] /api/proxy/urllib_proxy/urllib -> http://localhost:58062/urllib +0ms
// hc-proxy [POST] /api/proxy/urllib_proxy/urllib -> http://localhost:58062/urllib +3ms
// hc-proxy [DELETE] /api/proxy/urllib_proxy/urllib -> http://localhost:58062/urllib +1ms
// hc-proxy [PUT] /api/proxy/urllib_proxy/urllib -> http://localhost:58062/urllib +0ms
// ...
- 本地开发时,可以使用这个代理访问远程其它服务(如otm)的问题;
- 非本地开发环境时,可以不使用代理,而直接访问类似'/otm_v2/api/xxx',以减小内部调用开销,由前端自行控制;