代理请求时请求停止,除非我使用 http-proxy-middleware
Request stalls when proxying request unless I use http-proxy-middleware
我有如下供应商:
@Injectable()
export class GameServerProxyService {
private httpProxy: httpProxy;
constructor(@Inject(GameServerDetailsService) private gameServiceDetailsService: GameServerDetailsService) {
this.httpProxy = httpProxy.createProxyServer({});
this.httpProxy.on("proxyReq", (err,req,res)=>{
console.log("proxyReq");
});
this.httpProxy.on("proxyRes", (err,req,res)=>{
console.log("proxyRes");
});
this.httpProxy.on("error", (err,req,res)=>{
console.error(err);
});
}
proxyRequest(request: Request, response: Response){
console.log("proxyRequest");
this.httpProxy.web(request, response, {
target: "http://localhost:3100/",
changeOrigin: true,
}, (error) => {
console.log(error);
});
}
}
以及一个控制器,该控制器的路由使用此提供程序来代理调用:
@Controller()
export class SsrApiGatewayController {
constructor(
@Inject(GameServerProxyService) private gameServerProxyService: GameServerProxyService
) { }
@All("game-server/*")
async proxyGameServerRequest(@Req() request: Request, @Res() response: Response) {
console.log("Proxying Request..!")
this.gameServerProxyService.proxyRequest(request, response);
}
}
当我向这条路线发送请求时,我的请求停止了。我看到以下日志:
Proxy Request..!
proxyReq
所以代理请求永远不会到达响应阶段。我使用 Insomnia 发送请求。如果我取消停滞的请求,我在代理的服务器上会收到错误 (http://localhost:3100/):
[Nest] 6220 - 01/27/2022, 7:41:35 PM ERROR [ExceptionsHandler] request aborted
BadRequestError: request aborted
at IncomingMessage.onAborted (C:\******\node_modules\raw-body\index.js:231:10)
at IncomingMessage.emit (events.js:314:20)
at IncomingMessage.EventEmitter.emit (domain.js:483:12)
at abortIncoming (_http_server.js:533:9)
at socketOnClose (_http_server.js:526:3)
at Socket.emit (events.js:326:22)
at Socket.EventEmitter.emit (domain.js:483:12)
at TCP.<anonymous> (net.js:675:12)
很明显,我的请求被代理转发到另一台服务器,但我没有收到响应。
如果我改用 http-proxy-middleware
,效果很好。我这样注册中间件:
async function bootstrap() {
const app = await NestFactory.create(SsrApiGatewayModule);
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
}),
);
app.use("/game-server/*",
createProxyMiddleware({
target: "http://localhost:3100",
changeOrigin: true,
})
);
var configService = app.get(ConfigService);
var commonConfig = configService.get<CommonConfig>(CommonKey);
await app.listen(commonConfig.port);
}
代理请求处理正常。
我想使用我自己的提供者,因为我将有一个复杂的代理路由器 target
,它需要查询我计划注入的其他一些提供者。所以我想 'stay inside' 的 NestJS 'ecosystem' 通过编写我自己的提供商。
我不确定我的请求为什么会停滞。
我还尝试实现一个 NestJS 中间件(因此我可以保留将提供程序注入我的中间件的能力)并将 http-proxy-middleware
本身包装在我的中间件中,而不是我自己使用 http-proxy
。这以同样的方式失败。
原来问题是 BodyParser 中间件正在解析“消耗”底层数据流的请求主体。然后当我的代理代码运行时,它会尝试用它代理 fall 和请求主体,但由于数据流已被消耗而无法这样做。代理服务器无限期地等待请求数据,但它永远不会到达。
我的解决方案是编写自己的中间件来包装正文解析器和代理中间件。我根据请求 url 决定使用哪个 - 如果 URL 以 /game-server/
开头或以 /game-server
结尾,则使用代理,否则使用正文解析器。
为了完整起见,这里是代码:
引导:
const app = await NestFactory.create(SsrApiGatewayModule, {
//We will manually invoke body-parser in the API Gateway Proxy Middleware.
bodyParser: false,
});
根模块
@Module({
//...
})
export class SsrApiGatewayModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(ApiGatewayProxyMiddleware)
.forRoutes({ path: `*`, method: RequestMethod.ALL });
}
}
中间件:
@Injectable()
export class ApiGatewayProxyMiddleware implements NestMiddleware {
private jsonBodyParser: RequestHandler;
private httpProxyMiddleware: RequestHandler;
private startWith: string;
private endWidth: string;
//You can inject stuff here.
constructor() {
this.jsonBodyParser = bodyParser.json();
//You can inject the config module to configure target, etc
this.httpProxyMiddleware = createProxyMiddleware({
target: "http://localhost:3000",
changeOrigin: true,
pathRewrite: {'^/game-server' : ''},
})
this.startWith = `/game-server/`;
this.endWidth = `/game-server`;
}
use(req: Request, res: Response, next: NextFunction) {
let pathMatch = req.path.startsWith(this.startWith);
if(!pathMatch) pathMatch = req.path.endsWith(this.endWidth);
if(pathMatch) {
console.log("Proxying request for path: " + req.path)
this.httpProxyMiddleware(req,res,next);
}
else {
//Only run body parser if we plan to handle the request here instead of proxying it
console.log("Parsing request body for path: " + req.path);
this.jsonBodyParser(req, res, next);
}
}
}
我有如下供应商:
@Injectable()
export class GameServerProxyService {
private httpProxy: httpProxy;
constructor(@Inject(GameServerDetailsService) private gameServiceDetailsService: GameServerDetailsService) {
this.httpProxy = httpProxy.createProxyServer({});
this.httpProxy.on("proxyReq", (err,req,res)=>{
console.log("proxyReq");
});
this.httpProxy.on("proxyRes", (err,req,res)=>{
console.log("proxyRes");
});
this.httpProxy.on("error", (err,req,res)=>{
console.error(err);
});
}
proxyRequest(request: Request, response: Response){
console.log("proxyRequest");
this.httpProxy.web(request, response, {
target: "http://localhost:3100/",
changeOrigin: true,
}, (error) => {
console.log(error);
});
}
}
以及一个控制器,该控制器的路由使用此提供程序来代理调用:
@Controller()
export class SsrApiGatewayController {
constructor(
@Inject(GameServerProxyService) private gameServerProxyService: GameServerProxyService
) { }
@All("game-server/*")
async proxyGameServerRequest(@Req() request: Request, @Res() response: Response) {
console.log("Proxying Request..!")
this.gameServerProxyService.proxyRequest(request, response);
}
}
当我向这条路线发送请求时,我的请求停止了。我看到以下日志:
Proxy Request..!
proxyReq
所以代理请求永远不会到达响应阶段。我使用 Insomnia 发送请求。如果我取消停滞的请求,我在代理的服务器上会收到错误 (http://localhost:3100/):
[Nest] 6220 - 01/27/2022, 7:41:35 PM ERROR [ExceptionsHandler] request aborted
BadRequestError: request aborted
at IncomingMessage.onAborted (C:\******\node_modules\raw-body\index.js:231:10)
at IncomingMessage.emit (events.js:314:20)
at IncomingMessage.EventEmitter.emit (domain.js:483:12)
at abortIncoming (_http_server.js:533:9)
at socketOnClose (_http_server.js:526:3)
at Socket.emit (events.js:326:22)
at Socket.EventEmitter.emit (domain.js:483:12)
at TCP.<anonymous> (net.js:675:12)
很明显,我的请求被代理转发到另一台服务器,但我没有收到响应。
如果我改用 http-proxy-middleware
,效果很好。我这样注册中间件:
async function bootstrap() {
const app = await NestFactory.create(SsrApiGatewayModule);
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
}),
);
app.use("/game-server/*",
createProxyMiddleware({
target: "http://localhost:3100",
changeOrigin: true,
})
);
var configService = app.get(ConfigService);
var commonConfig = configService.get<CommonConfig>(CommonKey);
await app.listen(commonConfig.port);
}
代理请求处理正常。
我想使用我自己的提供者,因为我将有一个复杂的代理路由器 target
,它需要查询我计划注入的其他一些提供者。所以我想 'stay inside' 的 NestJS 'ecosystem' 通过编写我自己的提供商。
我不确定我的请求为什么会停滞。
我还尝试实现一个 NestJS 中间件(因此我可以保留将提供程序注入我的中间件的能力)并将 http-proxy-middleware
本身包装在我的中间件中,而不是我自己使用 http-proxy
。这以同样的方式失败。
原来问题是 BodyParser 中间件正在解析“消耗”底层数据流的请求主体。然后当我的代理代码运行时,它会尝试用它代理 fall 和请求主体,但由于数据流已被消耗而无法这样做。代理服务器无限期地等待请求数据,但它永远不会到达。
我的解决方案是编写自己的中间件来包装正文解析器和代理中间件。我根据请求 url 决定使用哪个 - 如果 URL 以 /game-server/
开头或以 /game-server
结尾,则使用代理,否则使用正文解析器。
为了完整起见,这里是代码:
引导:
const app = await NestFactory.create(SsrApiGatewayModule, {
//We will manually invoke body-parser in the API Gateway Proxy Middleware.
bodyParser: false,
});
根模块
@Module({
//...
})
export class SsrApiGatewayModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(ApiGatewayProxyMiddleware)
.forRoutes({ path: `*`, method: RequestMethod.ALL });
}
}
中间件:
@Injectable()
export class ApiGatewayProxyMiddleware implements NestMiddleware {
private jsonBodyParser: RequestHandler;
private httpProxyMiddleware: RequestHandler;
private startWith: string;
private endWidth: string;
//You can inject stuff here.
constructor() {
this.jsonBodyParser = bodyParser.json();
//You can inject the config module to configure target, etc
this.httpProxyMiddleware = createProxyMiddleware({
target: "http://localhost:3000",
changeOrigin: true,
pathRewrite: {'^/game-server' : ''},
})
this.startWith = `/game-server/`;
this.endWidth = `/game-server`;
}
use(req: Request, res: Response, next: NextFunction) {
let pathMatch = req.path.startsWith(this.startWith);
if(!pathMatch) pathMatch = req.path.endsWith(this.endWidth);
if(pathMatch) {
console.log("Proxying request for path: " + req.path)
this.httpProxyMiddleware(req,res,next);
}
else {
//Only run body parser if we plan to handle the request here instead of proxying it
console.log("Parsing request body for path: " + req.path);
this.jsonBodyParser(req, res, next);
}
}
}