GET 以外的代理请求挂在 NestJS 和 http-proxy-middleware 上

Proxied requests other than GET are hanging with NestJS and http-proxy-middleware

示例代码在 https://github.com/baumgarb/reverse-proxy-demo 上可用。README.md 解释了如果克隆存储库如何重现问题。

我有一个 API 网关和一个 returns 待办事项 (TodosAPI) 的下游服务。客户端通过 API 网关访问下游服务。

API 网关正在利用 http-proxy-middleware 包来代理请求。有两个实现,第二个不工作:

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。 NestMiddleware 在应用程序模块中注册,在路径 /api/v2/...

中启动

此方法适用于 GET 请求,但其他 http 方法(如 PUT)保持 "hanging" 且客户端从未收到任何响应。问题似乎是永远不会调用下游服务中的控制器。

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 });
  }
}

NestMiddleware 正在代理请求(我可以看到日志行显示 [NestMiddleware]: Proxying PUT request originally made to '/api/v2/todos-api/1'...)并且下游服务接收请求(我可以从日志记录中看到)。但是下游服务不会调用控制器/操作,最终也不会 returns。

有人知道我做错了什么吗?非常感谢!

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

所以解决方案是替换

const app = await NestFactory.create(AppModule);

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

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

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

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