使用@aws-sdk/client-lambda调用Lambda返回空响应排查
问题:Angular调用Lambda返回空Payload但CloudWatch显示执行成功
现象
- 在AWS控制台使用相同负载测试Lambda函数,可成功执行并返回有效结果
- 通过Angular调用时,CloudWatch日志明确显示Lambda运行成功,且输出了有效返回数据:
73ec2dbb-a647-43ea-82c6-a30407001d65 INFO event received: { body: "MATCH (n %7Bname:'Israel'%7D) RETURN n", headers: { SignatureHeader: '******' } } 73ec2dbb-a647-43ea-82c6-a30407001d65 INFO requestPayload: MATCH (n %7Bname:'Israel'%7D) RETURN n 73ec2dbb-a647-43ea-82c6-a30407001d65 INFO Successfully received result: { "results": [ { "n": { "~id": "1d68ae59-deef-4dd7-b97d-d2771f7dd793", "~entityType": "node", "~labels": [ "Country" ], "~properties": { "keyID": 972, "language": "Hebrew", "codeText": "IL", "image": "flags/il.png", "enabled": true, "name": "Israel" } } } ] }
- 但浏览器控制台显示Angular应用收到空结果集:
Lambda payload: {body: "MATCH (n %7Bname:'Israel'%7D) RETURN n", headers: {…}} Creating LambdaClient: {body: "MATCH (n %7Bname:'Israel'%7D) RETURN n", headers: {…}} SENDING LambdaClient. command: {FunctionName: 'simplify-mvp-neptune-query:dev', InvocationType: 'Event', Payload: Uint8Array(102)} Returned from LAmbda call. resp: Uint8Array [buffer: ArrayBuffer(0), byteLength: 0, byteOffset: 0, length: 0, Symbol(Symbol.toStringTag): 'Uint8Array']
Angular客户端调用代码
import { Lambda } from "@aws-sdk/client-lambda"; ... async queryLambda(data: any) { console.log('Creating LambdaClient: ', data); const lambdaClient = new Lambda({ region: environment.awsRegion, credentials: { accessKeyId: environment.awsAccessKeyLambda, secretAccessKey: environment.awsSecretLambda, } }); console.log('Creating InvokeCommand'); const command = { FunctionName: environment.awsLambdaDBFunction, InvocationType: 'Event', //Payload: Buffer.from(JSON.stringify(data)), Payload: new TextEncoder().encode(JSON.stringify(data)) }; try { console.log('SENDING LambdaClient. command: ', command); const resp = await lambdaClient.invoke(command); console.log('Returned from LAmbda call. resp: ', resp.Payload); //const result = Buffer.from(resp.Payload).toString(); const result = (new TextDecoder('utf-8').decode(resp.Payload)); console.log('Converted result from buffer: ', result); return result; } catch (err) { console.log('There was an error invoking Lambda DB function: ', err); return false; } }
也曾尝试另一种调用方式,结果一致:
import { LambdaClient, InvokeCommand } from "@aws-sdk/client-lambda"; ... const client = new LambdaClient(config); const command = new InvokeCommand(input); const response = await client.send(command);
Lambda(NodeJS 18.x)代码
import axios from 'axios'; import { SignatureV4 } from '@aws-sdk/signature-v4'; import { Sha256 } from '@aws-crypto/sha256-js'; const { AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, CALL_SIGNATURE } = process.env; const sigv4 = new SignatureV4({ service: 'neptune-db', region: 'us-east-1', credentials: { accessKeyId: AWS_ACCESS_KEY_ID, secretAccessKey: AWS_SECRET_ACCESS_KEY, sessionToken: AWS_SESSION_TOKEN, }, sha256: Sha256, }); const validateSignature = async (requestSignature) => { //Lambda call permission validator if (requestSignature!=CALL_SIGNATURE) { throw new Error('Function call signature does not match'); } }; const decodeQuery = async (queryText) => { //Decodes some chars in the raw query text for Lambda to process properly let result = queryText.replace(/%7B/g, "{"); result = result.replace(/%7D/g, "}"); return result; }; export const handler = async (event) => { // retrieve signature and payload const requestSignature = event.headers.SignatureHeader; //const requestSignature = event.body.SignatureHeader; console.log('event received: ', event); try { await validateSignature(requestSignature); // throws if invalid signature } catch (error) { console.error(error); return { statusCode: 400, body: `Function permission error: ${error}`, }; } const requestPayload = event.body; //Passed openCypher query string console.log('requestPayload: ', requestPayload); const signatureQuery = await decodeQuery(requestPayload); const ocQuery = requestPayload; const API_URL = "https://****.****.us-east-1.neptune.amazonaws.com:8182/openCypher?query=" + ocQuery; const apiUrl = new URL(API_URL); const signed = await sigv4.sign({ method: 'GET', hostname: apiUrl.host, path: apiUrl.pathname, protocol: apiUrl.protocol, query: { query: signatureQuery }, headers: { 'Content-Type': 'application/json', host: apiUrl.hostname, } }); try { const result = await axios({ ...signed, url: API_URL, }); console.log('Successfully received result: ', JSON.stringify(result.data)); return { statusCode: 200, body: result.data }; } catch (error) { console.log('An error occurred', error); throw error; } };
解决方案
问题出在**InvocationType: 'Event'**这个参数上:
- 当设置为
Event时,Lambda会以异步方式执行,调用方(Angular)会立即收到空响应,不会等待Lambda执行完成并返回结果,这就是你看到空Payload的原因 - 若需要同步获取Lambda执行结果,需将
InvocationType改为RequestResponse(这也是默认值)
修改后的Angular调用代码片段:
const command = { FunctionName: environment.awsLambdaDBFunction, InvocationType: 'RequestResponse', // 改为同步调用模式 Payload: new TextEncoder().encode(JSON.stringify(data)) };
另外建议调整Lambda返回逻辑,将结果序列化为JSON字符串,避免前端解析异常:
return { statusCode: 200, body: JSON.stringify(result.data) // 将对象转为JSON字符串 };
前端解码Payload后,可直接用JSON.parse(result)解析出结果对象。
内容的提问来源于stack exchange,提问作者Mor Sagmon
相关产品推荐
相关产品推荐

