当调用源自浏览器 AWS SDK 时,AWS Lambda 设置响应 headers
AWS Lambda set response headers when invoke originates from Browser AWS SDK
使用 API 网关与 AWS Lambda 代理集成设置响应 headers 很简单,看起来像这样:
import zlib from 'zlib';
exports.handler = async (event, context, callback) => {
const body = zlib.gzipSync(JSON.stringify({ data: 'mock' }));
const headers = {};
headers['Content-Type'] = 'application/json';
headers['Content-Encoding'] = 'gzip';
const responseObject = {
statusCode: 200,
headers,
body: body.toString('base64'),
isBase64Encoded: true
};
return callback(null, responseObject);
}
一切都按预期返回 gzipped。因为我们设置了 content-encoding 浏览器解压缩响应。
问题是在浏览器直接使用AWS SDK JS调用Lambda函数时如何设置headers? API 网关是在之前的设置中实现 headers 的服务,在 AWS Lambda 前面没有 API 网关 headers 被忽略并通常设置为:
access-control-allow-origin: *
access-control-expose-headers: x-amzn-RequestId,x-amzn-ErrorType,x-amzn-ErrorMessage,Date,x-amz-log-result,x-amz-function-error
content-length: 1242
content-type: application/json
date: Fri, 26 Apr 2019 00:36:35 GMT
status: 200
x-amz-executed-version: $LATEST
x-amzn-remapped-content-length: 0
x-amzn-requestid: <REDACTED>
x-amzn-trace-id: <REDACTED>
AWS SDK JS 浏览器调用代码如下所示:
import AWS from 'aws-sdk';
AWS.config.region = 'us-east-1'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: '<SOME IDENTITY>',
});
const AWSLambda = new AWS.Lambda({region: REGION, apiVersion: '2015-03-31'});
const parameters = {
FunctionName : 'MyFunctionName',
InvocationType : 'RequestResponse',
LogType : 'None',
Payload: JSON.stringify({msg: 'hello lambda'})
};
(async () => {
const response = await AWSLambda.invoke(shopParameters).promise();
console.log(response);
})();
返回的响应是上面的响应 object 作为带有通用 header 的字符串。浏览器不解压缩压缩后的内容,大概是因为 content-encoding header 没有被设置。从浏览器调用时,AWS Lambda 将整个 Lambda 响应 object 视为响应,并且不执行 API 网关发生的任何转换。例如,API 网关会选择响应 object 结构并将响应 object header 映射到响应,然后再发送给客户端。
没有 API 网关就无法设置 AWS Lambda headers 吗?或者是使用 https://github.com/nodeca/pako 之类的东西在客户端手动解压缩 gzip 内容的唯一选择(叹息)。
不使用 API 网关的想法来自此处找到的 AWS 文档,希望避免 API 网关成本:https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/browser-invoke-lambda-function-example.html
非常感谢任何指导、专业知识和想法!
不,如果没有 Lambda 服务 API 前面的 东西 就无法做到这一点——通常是 API 网关。
您的 const responseObject
实际上是使用 API 网关指定的格式创建响应 -- Lambda 服务不对响应进行解释。这就是为什么它对您的响应中的 headers 没有影响,以及为什么 base64 保持未翻译的原因——整个结构是 API 网关特定的。 Lambda 刚刚返回 JSON.
如果响应足够小,"something" 也可以是 Application Load Balancer which might or might not be easy enough to use with Cognito,但身份验证会有所不同。它使用与 API 网关基本相同的响应格式,平衡器在将其返回给浏览器之前解码 base64。
您还可以使用 CloudFront 的 Lambda@Edge 功能通过 HTTP(S) 调用 Lambda 函数,并设置自定义 headers 并自动解码 base64,但此服务没有 built-in Cognito 集成,与 full-featured Lambda 服务明显不同,只支持 Node.js 和 运行 Lambda 函数在离浏览器最近的 AWS 区域而不是它所在的区域是为了更好的全球性能而创建的。 Lambda@Edge 还需要一种不同的输出格式——它比 API 网关期望的响应结构更 well-engineered 设计,但因此它也不可互换。
或者,根据您的要求,我使用的策略是让 lambda 将 json 写入 s3,其中它的 public 可以轻松获取。
使用 API 网关与 AWS Lambda 代理集成设置响应 headers 很简单,看起来像这样:
import zlib from 'zlib';
exports.handler = async (event, context, callback) => {
const body = zlib.gzipSync(JSON.stringify({ data: 'mock' }));
const headers = {};
headers['Content-Type'] = 'application/json';
headers['Content-Encoding'] = 'gzip';
const responseObject = {
statusCode: 200,
headers,
body: body.toString('base64'),
isBase64Encoded: true
};
return callback(null, responseObject);
}
一切都按预期返回 gzipped。因为我们设置了 content-encoding 浏览器解压缩响应。
问题是在浏览器直接使用AWS SDK JS调用Lambda函数时如何设置headers? API 网关是在之前的设置中实现 headers 的服务,在 AWS Lambda 前面没有 API 网关 headers 被忽略并通常设置为:
access-control-allow-origin: *
access-control-expose-headers: x-amzn-RequestId,x-amzn-ErrorType,x-amzn-ErrorMessage,Date,x-amz-log-result,x-amz-function-error
content-length: 1242
content-type: application/json
date: Fri, 26 Apr 2019 00:36:35 GMT
status: 200
x-amz-executed-version: $LATEST
x-amzn-remapped-content-length: 0
x-amzn-requestid: <REDACTED>
x-amzn-trace-id: <REDACTED>
AWS SDK JS 浏览器调用代码如下所示:
import AWS from 'aws-sdk';
AWS.config.region = 'us-east-1'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: '<SOME IDENTITY>',
});
const AWSLambda = new AWS.Lambda({region: REGION, apiVersion: '2015-03-31'});
const parameters = {
FunctionName : 'MyFunctionName',
InvocationType : 'RequestResponse',
LogType : 'None',
Payload: JSON.stringify({msg: 'hello lambda'})
};
(async () => {
const response = await AWSLambda.invoke(shopParameters).promise();
console.log(response);
})();
返回的响应是上面的响应 object 作为带有通用 header 的字符串。浏览器不解压缩压缩后的内容,大概是因为 content-encoding header 没有被设置。从浏览器调用时,AWS Lambda 将整个 Lambda 响应 object 视为响应,并且不执行 API 网关发生的任何转换。例如,API 网关会选择响应 object 结构并将响应 object header 映射到响应,然后再发送给客户端。
没有 API 网关就无法设置 AWS Lambda headers 吗?或者是使用 https://github.com/nodeca/pako 之类的东西在客户端手动解压缩 gzip 内容的唯一选择(叹息)。
不使用 API 网关的想法来自此处找到的 AWS 文档,希望避免 API 网关成本:https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/browser-invoke-lambda-function-example.html
非常感谢任何指导、专业知识和想法!
不,如果没有 Lambda 服务 API 前面的 东西 就无法做到这一点——通常是 API 网关。
您的 const responseObject
实际上是使用 API 网关指定的格式创建响应 -- Lambda 服务不对响应进行解释。这就是为什么它对您的响应中的 headers 没有影响,以及为什么 base64 保持未翻译的原因——整个结构是 API 网关特定的。 Lambda 刚刚返回 JSON.
如果响应足够小,"something" 也可以是 Application Load Balancer which might or might not be easy enough to use with Cognito,但身份验证会有所不同。它使用与 API 网关基本相同的响应格式,平衡器在将其返回给浏览器之前解码 base64。
您还可以使用 CloudFront 的 Lambda@Edge 功能通过 HTTP(S) 调用 Lambda 函数,并设置自定义 headers 并自动解码 base64,但此服务没有 built-in Cognito 集成,与 full-featured Lambda 服务明显不同,只支持 Node.js 和 运行 Lambda 函数在离浏览器最近的 AWS 区域而不是它所在的区域是为了更好的全球性能而创建的。 Lambda@Edge 还需要一种不同的输出格式——它比 API 网关期望的响应结构更 well-engineered 设计,但因此它也不可互换。
或者,根据您的要求,我使用的策略是让 lambda 将 json 写入 s3,其中它的 public 可以轻松获取。