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 });
}
}
- 运行
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'...
)并且下游服务接收请求(我可以从日志记录中看到)。但是下游服务不会调用控制器/操作,最终也不会 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,并为其余端点启用它。
示例代码在 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 });
}
}
- 运行
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'...
)并且下游服务接收请求(我可以从日志记录中看到)。但是下游服务不会调用控制器/操作,最终也不会 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,并为其余端点启用它。