express.js 代理
Posted
技术标签:
【中文标题】express.js 代理【英文标题】:Proxy with express.js 【发布时间】:2012-05-13 04:56:24 【问题描述】:为避免同域 AJAX 问题,我希望我的 node.js Web 服务器将来自 URL /api/BLABLA
的所有请求转发到另一台服务器,例如 other_domain.com:3000/BLABLA
,并向用户返回此远程服务器返回的相同内容,透明的。
所有其他 URL(除了/api/*
)将直接提供,没有代理。
如何使用 node.js + express.js 实现这一点?能举个简单的代码例子吗?
(web服务器和远程3000
服务器都在我的控制之下,都运行node.js和express.js)
到目前为止,我发现了这个 https://github.com/http-party/node-http-proxy ,但是阅读那里的文档并没有让我变得更聪明。我最终得到了
var proxy = new httpProxy.RoutingProxy();
app.all("/api/*", function(req, res)
console.log("old request url " + req.url)
req.url = '/' + req.url.split('/').slice(2).join('/'); // remove the '/api' part
console.log("new request url " + req.url)
proxy.proxyRequest(req, res,
host: "other_domain.com",
port: 3000
);
);
但是没有任何东西返回到原始网络服务器(或最终用户),所以没有运气。
【问题讨论】:
你做的方式对我有用,没有任何修改 虽然回答有点晚了,但遇到了类似的问题并通过删除正文解析器来解决它,以便在进一步代理之前不会解析请求正文。 【参考方案1】:您想使用http.request
向远程 API 创建类似的请求并返回其响应。
类似这样的:
const http = require('http');
// or use import http from 'http';
/* your app config here */
app.post('/api/BLABLA', (oreq, ores) =>
const options =
// host to forward to
host: 'www.google.com',
// port to forward to
port: 80,
// path to forward to
path: '/api/BLABLA',
// request method
method: 'POST',
// headers to send
headers: oreq.headers,
;
const creq = http
.request(options, pres =>
// set encoding
pres.setEncoding('utf8');
// set http status code based on proxied response
ores.writeHead(pres.statusCode);
// wait for data
pres.on('data', chunk =>
ores.write(chunk);
);
pres.on('close', () =>
// closed, let's end client request as well
ores.end();
);
pres.on('end', () =>
// finished, let's finish client request as well
ores.end();
);
)
.on('error', e =>
// we got an error
console.log(e.message);
try
// attempt to set error message and http status
ores.writeHead(500);
ores.write(e.message);
catch (e)
// ignore
ores.end();
);
creq.end();
);
注意:我还没有真正尝试过上面的方法,所以它可能包含解析错误,希望这会给你一个关于如何让它工作的提示。
【讨论】:
是的,一些修改是必要的,但我更喜欢这个,而不是引入一个额外的新“代理”模块依赖项。有点冗长,但至少我确切地知道发生了什么。干杯。 好像需要在写入数据缝隙之前做 res.writeHead,否则会报错(headers 不能写在 body 之后)。 @user124114 - 请填写您使用过的完整解决方案 似乎您在设置标题时会遇到问题。Cannot render headers after they are sent to the client
我已经更新了 es6 语法的答案并修复了 writeHead 问题【参考方案2】:
request 自 2020 年 2 月起已弃用,由于历史原因,我将在下面留下答案,但请考虑改用此 issue 中列出的替代方案。
存档
我做了类似的事情,但我改用了request:
var request = require('request');
app.get('/', function(req,res)
//modify the url in any way you want
var newurl = 'http://google.com/';
request(newurl).pipe(res);
);
我希望这会有所帮助,我花了一段时间才意识到我可以做到这一点:)
【讨论】:
谢谢,比使用 Node.js 的 HTTP 请求简单得多 更简单,如果您还通过管道发送请求:***.com/questions/7559862/… 漂亮干净的解决方案。我发布了一个答案以使其也适用于 POST 请求(否则它不会将您的帖子正文转发到 API)。如果您编辑答案,我很乐意删除我的答案。 @Jonathan @trigoman 现在request
已被弃用(请参阅github.com/request/request 的通知),还有什么替代方案?
This answer 使用 node-fetch
帮助我解决了一个不推荐使用的解决方案。【参考方案3】:
扩展trigoman 的回答(他的全部学分)以使用 POST(也可以使用 PUT 等):
app.use('/api', function(req, res)
var url = 'YOUR_API_BASE_URL'+ req.url;
var r = null;
if(req.method === 'POST')
r = request.post(uri: url, json: req.body);
else
r = request(url);
req.pipe(r).pipe(res);
);
【讨论】:
无法使其与 PUT 一起使用。但适用于 GET 和 POST。谢谢!! @Protron 用于 PUT 请求只需使用类似if(req.method === 'PUT') r = request.put(uri: url, json: req.body);
如果您需要将标头作为 PUT 或 POST 请求的一部分传递,请确保删除 content-length 标头,以便请求可以计算它。否则,接收服务器可能会截断数据,这将导致错误。
@Henrik Peinar,当我发出登录帖子请求并期望从 web.com/api/login 重定向到 web.com/ 时,这会有所帮助吗【参考方案4】:
好的,这是使用 require('request') npm 模块和环境变量 * 而不是硬编码代理的准备复制粘贴答案):
咖啡脚本
app.use (req, res, next) ->
r = false
method = req.method.toLowerCase().replace(/delete/, 'del')
switch method
when 'get', 'post', 'del', 'put'
r = request[method](
uri: process.env.PROXY_URL + req.url
json: req.body)
else
return res.send('invalid method')
req.pipe(r).pipe res
app.use(function(req, res, next)
var method, r;
method = req.method.toLowerCase().replace(/delete/,"del");
switch (method)
case "get":
case "post":
case "del":
case "put":
r = request[method](
uri: process.env.PROXY_URL + req.url,
json: req.body
);
break;
default:
return res.send("invalid method");
return req.pipe(r).pipe(res);
);
【讨论】:
您可以先清理(例如,如果方法不在批准的方法),然后只做 r = request[method](/* the rest */);【参考方案5】:我找到了一个更短且非常简单的解决方案,它可以无缝运行,并且还可以使用express-http-proxy
进行身份验证:
const url = require('url');
const proxy = require('express-http-proxy');
// New hostname+path as specified by question:
const apiProxy = proxy('other_domain.com:3000/BLABLA',
proxyReqPathResolver: req => url.parse(req.baseUrl).path
);
然后简单地说:
app.use('/api/*', apiProxy);
注意:正如@MaxPRafferty 所述,使用req.originalUrl
代替baseUrl
来保留查询字符串:
forwardPath: req => url.parse(req.baseUrl).path
更新:正如 Andrew 所说(谢谢!),有一个使用相同原理的现成解决方案:
npm i --save http-proxy-middleware
然后:
const proxy = require('http-proxy-middleware')
var apiProxy = proxy('/api', target: 'http://www.example.org/api');
app.use(apiProxy)
文档:http-proxy-middleware on Github
我知道我加入这个聚会迟到了,但我希望这对某人有所帮助。
【讨论】:
req.url 没有完整的 url,所以更新了答案以使用 req.baseUrl 而不是 req.url 我也喜欢使用 req.originalUrl 代替 baseUrl 来保留查询字符串,但这可能并不总是理想的行为。 @MaxPRafferty - 无效评论。值得注意。谢谢。 这是最好的解决方案。我正在使用 http-proxy-middleware,但它是相同的概念。当已经有很棒的代理解决方案时,不要旋转自己的代理解决方案。 还有一个包使用起来更简单 npm install express-proxy-server --save var proxy = require('express-proxy-server'); app.use('/proxy', proxy('example.org/api'));【参考方案6】:我使用以下设置将/rest
上的所有内容定向到我的后端服务器(端口 8080),并将所有其他请求定向到前端服务器(端口 3001 上的 webpack 服务器)。它支持所有 HTTP 方法,不会丢失任何请求元信息并支持 websockets(我需要热重载)
var express = require('express');
var app = express();
var httpProxy = require('http-proxy');
var apiProxy = httpProxy.createProxyServer();
var backend = 'http://localhost:8080',
frontend = 'http://localhost:3001';
app.all("/rest/*", function(req, res)
apiProxy.web(req, res, target: backend);
);
app.all("/*", function(req, res)
apiProxy.web(req, res, target: frontend);
);
var server = require('http').createServer(app);
server.on('upgrade', function (req, socket, head)
apiProxy.ws(req, socket, head, target: frontend);
);
server.listen(3000);
【讨论】:
这是唯一一个也处理 web-sockets 的。【参考方案7】:我找到了一个更短的解决方案,完全符合我的要求https://github.com/http-party/node-http-proxy
安装后http-proxy
npm install http-proxy --save
在你的 server/index/app.js 中像下面这样使用它
var proxyServer = require('http-route-proxy');
app.use('/api/BLABLA/', proxyServer.connect(
to: 'other_domain.com:3000/BLABLA',
https: true,
route: ['/']
));
我真的花了好几天的时间到处寻找以避免这个问题,尝试了很多解决方案,但除了这个之外没有一个有效。
希望它也能帮助其他人:)
【讨论】:
【参考方案8】:首先安装 express 和 http-proxy-middleware
npm install express http-proxy-middleware --save
然后在你的 server.js 中
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use(express.static('client'));
// Add middleware for http proxying
const apiProxy = proxy('/api', target: 'http://localhost:8080' );
app.use('/api', apiProxy);
// Render your site
const renderIndex = (req, res) =>
res.sendFile(path.resolve(__dirname, 'client/index.html'));
app.get('/*', renderIndex);
app.listen(3000, () =>
console.log('Listening on: http://localhost:3000');
);
在此示例中,我们在端口 3000 上为站点提供服务,但是当请求以 /api 结尾时,我们将其重定向到 localhost:8080。
http://localhost:3000/api/login 重定向到http://localhost:8080/api/login
【讨论】:
【参考方案9】:我没有快递样品,但有一个简单的http-proxy
包装。我在博客中使用的代理的精简版。
简而言之,所有 nodejs http 代理包都在 http 协议级别工作,而不是 tcp(socket) 级别。对于 express 和所有 express 中间件也是如此。它们都不能做透明代理,也不能做 NAT,这意味着将传入的流量源 IP 保留在发送到后端 Web 服务器的数据包中。
但是,Web 服务器可以从 http x-forwarded 标头中获取原始 IP 并将其添加到日志中。
proxyOption
中的 xfwd: true
为 http-proxy
启用 x-forward 标头功能。
const url = require('url');
const proxy = require('http-proxy');
proxyConfig =
httpPort: 8888,
proxyOptions:
target:
host: 'example.com',
port: 80
,
xfwd: true // <--- This is what you are looking for.
;
function startProxy()
proxy
.createServer(proxyConfig.proxyOptions)
.listen(proxyConfig.httpPort, '0.0.0.0');
startProxy();
X-Forwarded 标头参考:https://en.wikipedia.org/wiki/X-Forwarded-For
我的代理的完整版:https://github.com/J-Siu/ghost-https-nodejs-proxy
【讨论】:
【参考方案10】:我认为你应该使用cors npm
const app = express();
const cors = require('cors');
var corsOptions =
origin: 'http://localhost:3000',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
app.use(cors(corsOptions));
https://www.npmjs.com/package/cors
【讨论】:
以上是关于express.js 代理的主要内容,如果未能解决你的问题,请参考以下文章
Apache:与 Apache 集成时 Express.js 中出现 502 代理错误 [关闭]
从 Nginx 到 express.js 上的 socket.io 的反向代理上的“无法获取”
为啥我们需要在 express.js 服务器上使用代理才能获得 webpack 热重载服务器功能与 react-routing 相结合
为啥我们需要在 express.js 服务器上使用代理才能获得与 react-routing 相结合的 webpack 热重载服务器功能