如何修复AWS Lambda函数中的'TypeError: Cannot read property 'origin' of undefined'错误
问题
测试AWS Lambda插入答案功能时,收到如下错误:
"errorType": "TypeError", "errorMessage": "Cannot read property 'origin' of undefined", "trace": [ "TypeError: Cannot read property 'origin' of undefined", " at responseHeaders (/var/task/index.js:32:26)", " at /var/task/index.js:124:22", " at processTicksAndRejections (internal/process/task_queues.js:95:5)" ]
相关Lambda代码如下:
/********************************************************************** * Upsert Answer item into table **********************************************************************/ /* POST test { "resource": "/", "path": "/", "httpMethod": "POST", "body": "{\"answer\":\"This is my super important answer\",\"questionSlug\":\"this-is-my-super-important-answer\"}" } PUT test - make sure to change the ID to one in your database before running or this method will create a new record { "resource": "/", "path": "/", "httpMethod": "PUT", "body": "{\"answer\":\"This is my super important answer\",\"negativeVotes\":0,\"positiveVotes\":1,\"id\":\"5ec9957e3f64b70004e7599e\"}" } */ // we need access to the AWS SDK var AWS = require("aws-sdk"); // we need uuid to generate unique ids const { v4: uuidv4 } = require("uuid"); // we need access to DynamoDB and choose the DocumentClient model var docClient = new AWS.DynamoDB.DocumentClient(); const responseHeaders = (headers) => { const origin = headers.origin || headers.Origin; return { // HTTP headers to pass back to the client "Content-Type": "application/json", // the next headers support CORS "X-Requested-With": "*", "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with", "Access-Control-Allow-Origin": origin, "Access-Control-Allow-Methods": "OPTIONS,*", Vary: "Origin", // for proxies // the "has-cors" library used by the Angular application wants this set "Access-Control-Allow-Credentials": "true", }; }; exports.handler = async (event) => { // get the HTTP Method used var httpMethod = event.httpMethod; // get the HTTP body sent var payload = JSON.parse(event.body); // time to prepare the upsert const paramQuery = async () => { // define our query let params = { TableName: "Answer", Key: { id: "" }, UpdateExpression: "set #qi = :qi, #a = :a, #nv = :nv, #pv = :pv", ExpressionAttributeNames: { // define the attributes used in the update expression "#qi": "questionId", "#a": "answer", "#nv": "negativeVotes", "#pv": "positiveVotes", }, ExpressionAttributeValues: { // set default values ":qi": "", ":a": "", ":nv": 0, ":pv": 0, }, // this tells DynamoDB to return the new records with all fields, not just the changed ones ReturnValues: "ALL_NEW", }; // these three fields can be set during create or update // set the answer if there is one if (payload.answer && payload.answer.trim()) params.ExpressionAttributeValues[":a"] = payload.answer; // set the category slug if there is one if (payload.questionId && payload.questionId.trim()) params.ExpressionAttributeValues[":qi"] = payload.questionId; if (httpMethod == "PUT") { // PUTs are updates but the ID is passed as a path paremeter // lets get the path parameters let pathParameters = event.pathParameters; // set the unique key of the item to be modified params.Key.id = pathParameters.id; // these two values are only changed - they are always defaulted during create // set the negativeVotes params.ExpressionAttributeValues[":nv"] = payload.negativeVotes; // set the positiveVotes params.ExpressionAttributeValues[":pv"] = payload.positiveVotes; } else { // POSTs are inserts // create and set the unique key. its a uuid without the '-' var id = uuidv4().replace(/\-/g, ""); params.Key.id = id; } // uncomment the next line to see the parameters as sent to DynamoDB //console.log(JSON.stringify(params)); // we create a promise to wrap the async DynamoDB execution return new Promise((resolve, reject) => { var queryParams = docClient.update(params).promise(); queryParams .then(function (data) { resolve({ statusCode: 200, body: JSON.stringify(data), // HTTP headers to pass back to the client headers: responseHeaders(event.headers), }); }) .catch(function (err) { reject(err); }); }); }; // we await our promise here and return the result (see the resolve above) return await paramQuery(); };
尝试给origin添加引号后,错误依然存在。
原因与解决方案
错误原因
错误出在responseHeaders函数的const origin = headers.origin || headers.Origin;这一行——当调用Lambda的event.headers为undefined时(比如在Lambda控制台测试时未传入headers,或某些调用场景下请求未携带headers),直接访问headers.origin就会触发Cannot read property 'origin' of undefined的类型错误。
解决方案
修改responseHeaders函数,增加对headers的空值判断,并给origin设置默认值(比如*,允许所有跨域请求),避免空指针异常:
const responseHeaders = (headers) => { // 处理headers为空的情况,默认赋值为空对象 headers = headers || {}; // 优先取origin/Origin,不存在则用*兜底 const origin = headers.origin || headers.Origin || '*'; return { "Content-Type": "application/json", "X-Requested-With": "*", "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with", "Access-Control-Allow-Origin": origin, "Access-Control-Allow-Methods": "OPTIONS,*", Vary: "Origin", "Access-Control-Allow-Credentials": "true", }; };
如果你的Node.js版本支持ES2020+,也可以用可选链操作符简化写法:
const origin = headers?.origin || headers?.Origin || '*';
另外,在Lambda控制台测试时,建议在测试事件中补充headers字段,模拟真实请求场景:
{ "resource": "/", "path": "/", "httpMethod": "POST", "headers": { "origin": "http://localhost:3000" }, "body": "{\"answer\":\"This is my super important answer\",\"questionSlug\":\"this-is-my-super-important-answer\"}" }
内容的提问来源于stack exchange,提问作者ShadowKnight
相关产品推荐
相关产品推荐

