【问题标题】:Proxied requests other than GET are hanging with NestJS and http-proxy-middlewareGET 以外的代理请求与 NestJS 和 http-proxy-middleware 挂起
【发布时间】:2020-03-02 17:59:05
【问题描述】:

https://github.com/baumgarb/reverse-proxy-demo 上提供了示例代码。README.md 说明了如何在克隆存储库时重现问题。

我有一个 API 网关和一个返回 todos (TodosAPI) 的下游服务。客户端通过 API Gateway 访问下游服务。

API 网关正在利用 http-proxy-middleware 包来代理请求。有两种实现方式,第 2 种方式不起作用:

1. main.ts 中的全局中间件,在路径 /api/v1/...

上启动

这种方法工作得非常好,它代理所有请求到下游服务,无论是什么 http 方法(GET、PUT、...)。

import * as proxy from 'http-proxy-middleware';

app.use(
  '/api/v1/todos-api',
  proxy({
    target: 'http://localhost:8090/api',
    pathRewrite: {
      '/api/v1/todos-api': ''
    },
    secure: false,
    onProxyReq: (proxyReq, req, res) => {
      console.log(
        `[Global Functional Middlware]: Proxying ${req.method} request originally made to '${req.originalUrl}'...`
      );
    }
  })
);

2。在路径 /api/v2/... 启动的应用程序模块中注册的 NestMiddleware...

这种方法适用于 GET 请求,但其他 http 方法(如 PUT)会一直“挂起”,并且客户端从未收到任何响应。问题似乎是下游服务中的控制器从未被调用过。

import * as proxy from 'http-proxy-middleware';

export class ReverseProxyMiddleware implements NestMiddleware {
  private proxy = proxy({
    target: 'http://localhost:8090/api',
    pathRewrite: {
      '/api/v2/todos-api': ''
    },
    secure: false,
    onProxyReq: (proxyReq, req, res) => {
      console.log(
        `[NestMiddleware]: Proxying ${req.method} request originally made to '${req.originalUrl}'...`
      );
    }
  });

  use(req: Request, res: Response, next: () => void) {
    this.proxy(req, res, next);
  }
}

并且这个中间件注册如下:

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService]
})
export class AppModule implements NestModule {
  configure(consumer: import('@nestjs/common').MiddlewareConsumer) {
    consumer
      .apply(ReverseProxyMiddleware)
      .forRoutes({ path: 'v2/todos-api', method: RequestMethod.ALL });
  }
}
  • 运行 curl -X PUT -H "Content-Type: application/json" -d "{\"id\": 1, \"userId\": 1, \"title\": \"delectus aut autem - v1\", \"completed\": true}" http://localhost:8080/api/v1/todos-api/1 运行良好
  • 运行 curl -X PUT -H "Content-Type: application/json" -d "{\"id\": 1, \"userId\": 1, \"title\": \"delectus aut autem - v2\", \"completed\": true}" http://localhost:8080/api/v2/todos-api/1 时遇到下游服务中的控制器永远不会被调用的问题

NestMiddleware 正在代理请求(我可以看到一条日志行说[NestMiddleware]: Proxying PUT request originally made to '/api/v2/todos-api/1'...),下游服务接收到请求(我可以从日志中看到)。但是下游服务不会调用控制器/动作,最终永远不会返回。

有人知道我在这里做错了什么吗?提前非常感谢!

【问题讨论】:

  • 老实说,这并不是真正要发表评论的东西,但这并不是一个真正的答案,所以:我已经尝试调查这个并检查了通过两个代理传入的内容(全局和 Nest 中间件之一)和 reqs 看起来相同。我感觉由于 http-proxy-middleware 包的底层结构以及该中间件的工作原理,存在一些问题。默认情况下,Nest 不一定会处理错误中间件,我感觉它正在以某种方式处理这个问题。这可能需要在 github 存储库中作为问题提出。
  • 非常感谢您的回复和透彻的分析。当您说“请求看起来相同”时:您是否检查了下游服务端的传入请求?如果是的话,我想我迷路了。因为如果它们在下游服务方面是相同的,那么我想不出任何解释为什么下游服务中的操作不会被调用。
  • 是的,只是在被代理的服务器上的全局中间件中做了一个简单的console.log(req),几乎没有发现任何差异。如果是这种情况,我认为中间件会尝试处理某种请求后功能,但由于 Nest 的设置方式而无法处理。有趣的想法是在拦截器而不是中间件中尝试这个,看看它是否在那里工作。
  • 这里是一个愚蠢的问题——带有application/json content-type 的“除了get”让我认为这是一个cors 问题。也许 Nest 没有处理 cors 错误?你试过用邮递员打 v2 吗?
  • 不,这里没有 CORS 问题。没有涉及 Web 上下文的客户端会阻止跨域请求。这是一个使用 curl 完成的简单 API 调用。你也可以用 Postman 或者 Insomnia 来测试,和 curl 没什么区别。

标签: middleware nestjs http-proxy-middleware


【解决方案1】:

我终于找到了问题所在。它似乎与body parser有关。如果我更改 API 网关以关闭正文解析器,则请求将成功转发。

所以解决办法是替换

const app = await NestFactory.create(AppModule);

const app = await NestFactory.create(AppModule, { bodyParser: false });

我还在实施修复的 Git 存储库中创建了一个分支 issue-fixed

【讨论】:

    【解决方案2】:

    在创建 Nest 应用程序时设置 bodyParser: false 只是解决我们正在代理的端点的问题,它会导致其他端点(例如:JWT localAuth)失败,因为它们需要解析正文。

    解决方案是创建一个如this answer 中所述的中间件,为您正在代理的特定端点禁用 bodyParser 并为其余端点启用它。

    【讨论】:

    • 您能否提供一个中间件的示例,该中间件会针对特定端点禁用bodyParser
    猜你喜欢
    • 2017-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多