9

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

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

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

main.ts1.在路径 /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'...),下游服务接收请求(我可以从日志中看到)。但是下游服务不会调用控制器/动作,最终永远不会返回。

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

4

2 回答 2

7

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

所以解决方案是更换

const app = await NestFactory.create(AppModule);

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

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

于 2019-11-25T07:27:55.903 回答
5

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

解决方案是按照此答案中的描述创建一个中间件,为您代理的特定端点禁用 bodyParser 并为其余端点启用它。

于 2020-11-20T15:02:38.523 回答