AWS API Gateway WebSocket:集成后端中缺少转换后的请求模板请求正文

AWS API Gatewat WebSocket: Request Templates body of request after transformations is missing in integration backend

我正在 AWS 上构建我的应用程序,我的应用程序使用这样的 websocket:

前端 WebSocket 客户端 ---> AWS API 网关 Websocket API ----> EC2 实例中的后端。

现在,为了让我的后端 Express 代码知道如何向特定客户端发送消息,我让它知道 websocket 客户端/用户的 connectionId。我正在关注这两个答案:

里面解释的很清楚了

下面是我在 AWS 中的配置 API Gateway WebSocket API:

我所做的是使用请求模板,匹配所有传入请求,转换将作为请求正文发送到我的集成端点的内容。 最终我想保留原始请求,同时在其上添加属性(如连接 ID)。 出于测试目的,我设置了以下模板:

{
    "myConnectionId": "$context.connectionId",
    "body": "$context"
}

这有效,我可以在 AWS CloudWatch 中检查转换后的请求正文是

{
   "myConnectionId":"MFEdof95tjMCK0w=",
   "body":"{routeKey=$connect, disconnectStatusCode=null, messageId=null, eventType=CONNECT, extendedRequestId=MFEdoFJPtjMF64Q=, requestTime=17/Jan/2022:07:29:01 +0000, messageDirection=IN, disconnectReason=null, stage=production, connectedAt=1642404541417, requestTimeEpoch=1642404541418, identity={cognitoIdentityPoolId=null, cognitoIdentityId=null, principalOrgId=null, cognitoAuthenticationType=null, userArn=null, userAgent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36, accountId=null, caller=null, sourceIp=219.102.102.145, accessKey=null, cognitoAuthenticationProvider=null, user=null}, requestId=MFEdoFJPtjMF64Q=, domainName=123.execute-api.ap-northeast-1.amazonaws.com, connectionId=MFEdof95tjMCK0w=, apiId=hd5zymklr8}"
}

然而,在我的 EC2 实例中的 Express 后端 运行 中,即使端点被触发,请求正文是空的并且没有地方可以找到 myConnectionId

后端:NodeJS / ExpressJS,在 index.ts:

  app.get('/connect', function(_req, res) {
    logger.info(`/connect _req: ${Object.keys(_req)}`);
    logger.info(`/connect _req.query: ${JSON.stringify(_req.query)}`);
    logger.info(`/connect _req.params: ${JSON.stringify(_req.params)}`);
    logger.info(`/connect _req.body: ${JSON.stringify(_req.body)}`);
    logger.info(`/connect _req.headers: ${JSON.stringify(_req.headers)}`);
    res.send('/connect hahaha success');
  });

日志输出:

2022-Jan-17 05:05:00:50  info: /connect _req: _readableState,_events,_eventsCount,_maxListeners,socket,httpVersionMajor,httpVersionMinor,httpVersion,complete,rawHeaders,rawTrailers,aborted,upgrade,url,method,statusCode,statusMessage,client,_consuming,_dumped,next,baseUrl,originalUrl,_parsedUrl,params,query,res,_startAt,_startTime,_remoteAddress,body,_parsedOriginalUrl,route
2022-Jan-17 05:05:00:50  info: /connect _req.query: {}
2022-Jan-17 05:05:00:50  info: /connect _req.params: {}
2022-Jan-17 05:05:00:50  info: /connect _req.body: {}
2022-Jan-17 05:05:00:50  info: /connect _req.headers: {"x-amzn-apigateway-api-id":"hd5zymklr8","x-amzn-trace-id":"Root=1-61e5232c-626a05ff264650183a73c98a","user-agent":"AmazonAPIGateway_hd5zymklr8","content-type":"application/json","accept":"application/json","host":"NLB-docloud-internal-ea0692d1e2c8186c.elb.ap-northeast-1.amazonaws.com","connection":"Keep-Alive"}

请求确实发送到了端点,但是为什么请求体是空的?

内容是怎么丢失的?

我对此做了一些研究。

正如在 post: AWS API Gateway Websockets -- where is the connectionID? 中讨论的那样,接受的答案(我在上面的问题中尝试的解决方案)假设您使用 Lambda 作为后端集成。

但是,我在 EC2 实例和 VPC Link 中使用 Express JS 运行。

在我的例子中,这个问题实际上是由一个被赞成的答案(但不是被接受的答案)解决的:https://whosebug.com/a/65639742/3703783.

官方文档在这里:Setting up data mapping for WebSocket APIs.

更新

上述方法会将connectionId添加到集成请求的header中,但是只有connectionId是不够的——我们还需要用户名等信息,以便后端能够知道哪个connectionId在向特定用户发送消息时使用。这将做:

aws apigatewayv2 update-integration 
--integration-id xxx 
--api-id xxx 
--request-parameters 'integration.request.header.userinfo'='route.request.body' 

此外,我还发现在我原来的问题中,而不是做

{
    "myConnectionId": "$context.connectionId",
    "body": "$context"
}

我应该简单地做:

{
  "integration.request.header.connectionId":"$context.connectionId",
}

这会将 connectionId 添加到请求 header。