service proxy
api代理模块,解决跨域、权限问题
场景案例
远程需要被代理转发的服务:
http://localhost:7001/api/aaa // 视频服务接口
http://localhost:7001/api/c/* // 视频服务接口
http://192.168.1.1:7001/api/bbb // 音乐服务接口
http://localhost:7001/ws/* // 聊天服务接口 (使用websocket)
以honeycomb项目为例进行配置
- 确定honeycomb项目的启动端口和prefix,比如: 项目的启动端口为8001,prefix为 'example'
- 整理远程调用的服务,给每个服务起个英文名称,比如: 上面的两个服务 视频服务(video) 音乐服务(music) 聊天服务(chat)
- 配置在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/*'
]
},
music: {
endpoint: 'http://192.168.1.1:7001/',
client: 'http',
api: [
'/api/bbb'
]
}
},
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/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
代理websocket服务
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/*'
]
}
},
headers: [
'x-csrf-token',
'X-Operator'
]
});
proxyInstance.setProxyPrefix('/api/proxy');
module.exports = function (router) {
proxyInstance.mount(router, app);
};
websocket配置完成后访问地址
访问地址为 ${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
代理文件上传
hc-proxy在1.3.0以后支持文件上传代理,使用方式如下:
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/upload',
file: true
},
{
path: '/api/upload_limited',
file: {
maxFileSize: 100
}
}
]
}
},
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
API document
约定:
- proxy挂在的http服务称为 "代理服务"
- 被proxy代理的远端服务称为 "远端服务"
new Proxy(options)
options Object
options.service 详情
{
${serviceCode}: {
endpoint: ${endpoint},
accessKeyId: ${accessKeyId},
accessKeySecret: ${accessKeySecret},
headerExtension: ${headerExtension},
headers: ${headers},
client: ${client},
timeout: ${timeout},
api: [
${apiString},
{
path: ${path},
route: ${route},
method: ${method},
timeout: ${apiTimeout},
defaultQuery: ${defaultQuery},
beforeRequest: ${beforeRequest}, // TODO
}
]
}
}
通用配置:
- endpoint: 每个远端服务的服务地址,如: 'http://localhost:7001' 或 'http://localhost:7001/service'
- client: 目前只支持'appClient' / 'http' / 'websocket',默认为'appClient',其中 appClient 带了honeycomb体系中的签名逻辑
- timeout: 某个服务或接口的超时时间,毫秒计算,默认60000
- apiString: 使用默认配置对某个api进行代理,设置的是Api的path,如: '/api/user' 会对 '/api/user' 进行 'GET', 'POST', 'PUT', 'DELETE' 代理
- path: api的路径,如: '/api/user',支持 '/api/user' / '/api/user/:user' / '/api/user/*'
- route: api在路由中出现的路径(会忽略proxyPrefix),如: '/remote_service/aaa' , 则调用 ${localService} + '/remote_service/aaa',会被走path对应的远端服务
- method: 指定这个api支持的方法 'GET' / ['GET', 'POST'],不填时,默认为 ['GET', 'POST', 'PUT', 'DELETE']
- apiTimeout: 覆盖整个服务的timeout
- defaultQuery: String / Object,用于配置默认的query参数(代理请求时自动加上)
- beforeRequest: 暂不支持
- file: 配置上传文件,出现上传文件配置时,该接口支持上传文件,子配置使用默认值可以配置为 {... file: true}
- maxFileSize: 上传文件的大小限制, 单位byte, default
client=[appClient/serviceClient]的专属配置
options.headers 用于声明proxy需要转发的header。
默认情况下,proxy不转发客户端过来的header,只有在proxyHeaders中配置的header才会被转发。
proxy.setProxyPrefix(proxyPrefix)
setProxyPrefix方法用于指定 hc-proxy 挂载在代理服务上的总前缀
如: 默认 proxyPrefix = /api/proxy
则: 所有请求远端服务的请求,格式为 ${localHttpServer}/api/proxy/${serviceCode}/${remoteApi}
proxy.mount(router, app)
将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',
timeout: 10000,
api: [
'/api/*',
'/otm_v2/api/entities/list',
{path: '/otm_v2/api/entities/list', method: 'GET'},
{path: '/otm_v2/api/entities/list', client: 'appClient'},
{path: '/otm_v2/api/schemas', method: ['GET', 'POST']},
{host: 'http://taobao.com/api', path: '/tag_factory_v2/api/:id', method: 'GET'},
{path: 'http://taobao.com/api', route: '/api/proxy/taobao_api'},
'/otm_v2/api/tags/query_tags_entity_pagenum',
{path: '/otm_v2/api/schemas/:id', route: '/api/proxy/schemas/:id'}
]
},
websocket: {
endpoint: 'http://dev.dtboost.biz.aliyun.test/websocket',
client: 'websocket',
api: [
'/ws'
]
}
},
headers: [
'x-csrf-token',
'X-Operator'
]
});
proxyInstance.setProxyPrefix('/api/proxy');
module.exports = function (router) {
proxyInstance.mount(router, app);
};
DEBUG [代理没生效?]
在命令行启动命令前加入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',以减小内部调用开销,由前端自行控制;