如何通过 API Gateway 向异步 Lambda 函数传递参数?

huangapple go评论95阅读模式
英文:

How do I pass arguments to an async Lambda function through API Gateway?

问题

我们有一个 AWS Lambda 函数,该函数调用 OpenAI API 来生成基于“transcript”的“Page”的“page_content”。一旦接收到响应,DynamoDB 表中相应的“Page”元素将被更新。

import boto3
import json
import openai

def lambda_handler(event, context):
   dynamodb = boto3.client('dynamodb')
   openai.api_key = "XXXXX"

   d = json.loads(event['body'])
   pageId = d['page_id']
   transcript = d['transcript']

   conversation = [
      {"role": "system", "content": "Assistant"},
      {"role": "user", "content": transcript},
      {"role": "system", "content":"Provide a blog post for the content above."}
   ]

   response = openai.ChatCompletion.create(
      model="gpt-3.5-turbo",
      messages=conversation,
      temperature=1,
      max_tokens=2000,
      top_p=1,
      frequency_penalty=0,
      presence_penalty=0
   )

   page_content = response['choices'][0]['message']['content']
   state = "COMPLETE"

   response = dynamodb.update_item(
      TableName='Pagedb-XXXXXX-dev',
      Key={'id': {'S': pageId}},
      UpdateExpression='SET content = :val1, contentState = :val2',
      ExpressionAttributeValues={
         ':val1': {'S': page_content},
         ':val2': {'S': state}
      },
   )

   return {
      'statusCode': 200,
      'headers': {
         'Content-Type': 'application/json', 
         'Access-Control-Allow-Origin': '*',
         'Access-Control-Allow-Headers': 'Content-Type',
         'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
       }
    }

此 Lambda 函数通过 API Gateway 从 React 应用程序调用。

const response = await axios.post(
      'https://XXXX.execute-api.us-east-2.amazonaws.com/default/generate-content',
      {page_id: page.id, transcript: page.transcript},
    );

问题: 大多数调用超出了 API Gateway 的 30 秒最大超时时间,尽管 Lambda 函数已成功完成,但会导致错误。为了解决这个问题,我们通过禁用“使用 Lambda 代理集成”并添加一个从“Event”映射而来的 HTTP 标头 X-Amz-Invocation-Type,使调用异步化。这允许我们的 Lambda 函数运行超过 30 秒,但我们不能再通过处理程序函数的“event”传递详细信息,因为 Lambda 代理允许这样做。

问题: 如果禁用了 Lambda Proxy 集成,我们如何从 React 应用程序将详细信息传递给 Lambda 函数通过 API Gateway?

更新 - 已实施的修复:
根据 Mark 的反馈,我为 API Gateway 方法的集成请求设置了一个映射模板,步骤如下:

  1. 集成请求 -> 映射模板 -> 将“Request body passthrough”设置为“当未定义模板时”
  2. “添加映射模板” -> 输入“application/json”
  3. 生成模板:方法请求 passthrough
  4. 修改为以下内容:
#set($allParams = $input.params())
{
   "method" : "$context.httpMethod", ## API method
   "authcontext" : "$context.authorizer.stringkey", ## Optional output from Lambda Authorizers

   ## passthrough body
   "body" : $input.json('$'),

   ## passthrough headers, querystrings and path parameters
   "params" : {
      #foreach($type in $allParams.keySet())
      #set($params = $allParams.get($type))
      "$type" : {
         #foreach($paramName in $params.keySet())
         "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
         #if($foreach.hasNext),#end
         #end
      }
      #if($foreach.hasNext),#end
      #end
   }
}
  1. 更新我的 Lambda 函数以设置变量如下:
pageId = event['body']['page_id']
transcript = event['body']['transcript']
英文:

We have an AWS Lambda function that makes a call to the OpenAI API to generate page_content for a Page based on a transcript. Once a response is received, the respective Page element in a DynamoDB table is updated.

import boto3
import json
import openai
    
    def lambda_handler(event, context):
       dynamodb = boto3.client('dynamodb')
       openai.api_key = "XXXXX"

       d = json.loads(event['body'])
       pageId = d['page_id']
       transcript = d['transcript']
           
       conversation = [
          {"role": "system", "content": "Assistant"},
          {"role": "user", "content": transcript},
          {"role": "system", "content":"Provide a blog post for the content above."}
       ]

       response = openai.ChatCompletion.create(
          model="gpt-3.5-turbo",
          messages=conversation,
          temperature=1,
          max_tokens=2000,
          top_p=1,
          frequency_penalty=0,
          presence_penalty=0
       )
    
       page_content = response['choices'][0]['message']['content']
       state = "COMPLETE"
    
       response = dynamodb.update_item(
          TableName='Pagedb-XXXXXX-dev',
          Key={'id': {'S': pageId}},
          UpdateExpression='SET content = :val1, contentState = :val2',
          ExpressionAttributeValues={
             ':val1': {'S': page_content},
             ':val2': {'S': state}
          },
       )
    
       return {
          'statusCode': 200,
          'headers': {
             'Content-Type': 'application/json', 
             'Access-Control-Allow-Origin': '*',
             'Access-Control-Allow-Headers': 'Content-Type',
             'Access-Control-Allow-Origin': '*',
             'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
           }
        }

This Lambda function is invoked through API Gateway from a React application.

    const response = await axios.post(
          'https://XXXX.execute-api.us-east-2.amazonaws.com/default/generate-content',
          {page_id: page.id, transcript: page.transcript},
        );

The Issue: Most of our calls exceed the 30 second maximum timeout for API Gateway and will cause an error, despite the Lambda function going to successful completion. To work around this, we made the call asynchronous by disabling "Use Lambda Proxy Integration" and adding an HTTP Header X-Amz-Invocation-Type mapped from 'Event'. This allows our Lambda function to run beyond 30 seconds, however we cannot pass details through 'event' of the handler function anymore, as lambda proxy allowed for this.

Question: How can we pass details from our React application to the Lambda function through API Gateway if Lambda Proxy integration is disabled?

Update - Implemented Fix
With Mark's feedback, I setup a Mapping Template for the Integration Request of my method in API Gateway doing the following:

  1. Integration Request -> Mapping Templates -> Set "Request body passthrough" to "When there are no templates defined"
  2. "Add mapping template" -> entered "application/json"
  3. Generate template: Method Request passthrough
  4. Modified to the following:
#set($allParams = $input.params())
{
   "method" : "$context.httpMethod", ## API method
   "authcontext" : "$context.authorizer.stringkey", ## Optional output from Lambda Authorizers

   ## passthrough body
   "body" : $input.json('$'),

   ## passthrough headers, querystrings and path parameters
   "params" : {
      #foreach($type in $allParams.keySet())
      #set($params = $allParams.get($type))
      "$type" : {
         #foreach($paramName in $params.keySet())
         "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
         #if($foreach.hasNext),#end
         #end
      }
      #if($foreach.hasNext),#end
      #end
   }
}
  1. Updated my Lambda function to set the variables as such
    pageId = event['body']['page_id']
    transcript = event['body']['transcript']

答案1

得分: 1

当您禁用代理集成时,您还禁用了默认的代理请求映射。您需要启用集成透传请求映射,以便再次在Lambda函数的event参数中显示该请求数据。

英文:

When you disabled the proxy integration, you also disabled the default proxy request mapping. You need to enable integration passthrough request mapping to get that request data showing up in the Lambda function's event parameter again.

huangapple
  • 本文由 发表于 2023年7月28日 03:36:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76782909.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定