使用Node Express代理JSON请求

5
我使用以下node-express代码将请求从Web服务器代理到API服务器:
``` app.use('/api', createProxyMiddleware({ target: 'http://api.example.com', changeOrigin: true })); ```
此代码将所有以“/api”开头的请求代理到“http://api.example.com”。其中,“changeOrigin”选项用于更改请求的来源,以便API服务器可以正确地返回响应。
app.use('/api', function(req, res) {
  var url = 'http://my.domain.com/api' + req.url;
  req.pipe(request(url)).pipe(res);
});

对于任何动词(get、post等),这都很适用,但一旦我发送'Content-type': 'application/json'请求,它会在pipe行上挂起。

为什么这个简单的node-express代理代码会在json请求上挂起?
如何修改它以支持它们?


1
你在那个路由之前使用了一个身体解析中间件,它已经读取了所有的请求数据吗? - mscdex
@mscdex 我正在开发一个由angular-fullstack yeoman生成的项目。我看到该项目使用了body-parser包,所以答案很可能是肯定的。 - Jonathan Livni
1
尝试将您的自定义中间件移至在解析请求体中间件之前(甚至将其作为第一个中间件),然后查看是否有所帮助。 - mscdex
@mscdex,你能解释一下为什么 body-parser 会导致代理挂起吗? - Jonathan Livni
“移动中间件”意味着在调用中间件之前设置代理。中间件(代理/ bodyparser / ...)的调用顺序对于表达式至关重要。如果您将其视为堆栈,其中每个请求从堆栈帧到堆栈帧,您可能会有一个想法。 - Stephan Hoyer
显示剩余2条评论
3个回答

18

您需要将那个自定义中间件函数移动到此行代码之前,以便在任何body解析器之前执行它。这样可以确保请求数据仍然在那里,可以在自定义中间件中将其传输到request(url)

当前挂起的原因是req没有数据可以写入request(url)(因为body解析中间件已经读取并解析了所有的请求数据),因此它从不调用.end()来结束request(url)流。这意味着对url的请求永远不会完成,因为它只是等待它永远不会收到的数据。


在我看来,抛出错误会更好的设计。我也期望 body-parser 只解析数据而不从 req 中提取它。 - Jonathan Livni
为了解析请求,必须从请求流中读取数据。一旦读取了所有请求数据,它就消失了。使用Streams2,你可能能够将所有数据推回到请求流中,但这意味着你必须缓冲所有数据,如果有人上传了一个巨大的文件,那么这可能会非常棘手。 - mscdex
两个问题:1)body-parser是否提供对解析后的JSON的访问?并且我能否将其重新流式传输到request(url)中?2)routes在express之后被调用,可能有原因,是什么?如果是这样,我真的应该先代理,然后再处理和路由吗?代理不应该是本地处理后路由的一部分吗? - Jonathan Livni
3
  1. body-parser 会将解析的结果放在 req.body 下面,所以你可以使用 JSON.stringify() 将其转换并写入到你的出站请求中。
  2. Express 在请求到达时按顺序评估所有中间件和路由。在这种情况下,config/express 只是添加了一堆中间件(用于处理 body、cookie 和 session 解析,静态文件服务等),这些中间件会在任何路由之前首先执行,以便路由可以获取已解析的 body 内容等。如果你自定义的 /api 中间件需要一个有效的会话,那么你需要修改堆栈顺序以满足你的需求。
- mscdex
是你在评论中提出的建议1对我有用。谢谢。 :) - Don Branson

4

对于POST请求,以下结构有效:

app.post('/api/method', (req, res) => {
  req.pipe(request.post(someUrl, { json: true, body: req.body }), { end: false }).pipe(res);
}

当然,如果您正在使用bodyparser中间件,则这是相关的。

1
这正是我一直在寻找的。发布一些JSON,通过express代理它,返回API的响应。太棒了! - Richard

-1

app.use('/api', function(req, res) {
  var url = 'http://my.domain.com/api' + req.url;
  
  request({
          uri: url,
          method: "POST",
          body: _body,
          json: true
      }, function (_err, _res, _resBody) {
          //do somethings
          res.json(_resBody);
      });

});


你应该解释为什么你的代码是一个改进。 - bright-star

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接