非Express环境下Node.js(Lambda)实现HTTP压缩并适配Postman
问题解答
关于compression库的使用
compression是Express专属的中间件,依赖Express的请求/响应处理流程,原生Node.js(包括AWS Lambda这种无服务器环境)没法直接用。没必要强行引入它,用Node.js内置的zlib模块完全能满足需求,还能减少依赖,更适合Lambda的轻量场景。
解决Postman/浏览器自动解析压缩响应的问题
你当前的问题出在两个核心点:
- Lambda返回二进制压缩数据时,未设置
isBase64Encoded: true,导致API Gateway把压缩后的Buffer转成Base64字符串返回 Content-Encoding头设置错误,同时写了gzip, deflate,但实际只用了deflate,且没有根据客户端支持的格式动态选择
具体修改步骤
- 解析客户端压缩支持:从请求头
Accept-Encoding判断客户端能接受的压缩格式(优先选gzip,压缩率更高) - 正确处理压缩数据:用zlib生成压缩Buffer,设置
isBase64Encoded: true告诉API Gateway这是二进制数据 - 匹配正确响应头:只设置实际使用的压缩格式到
Content-Encoding - 保留合理ETag逻辑:ETag基于原始未压缩内容生成,确保缓存有效性
修改后的代码
import { APIGatewayProxyEvent } from 'aws-lambda' import { getInfoFromDB } from './db/dbQueries' import crypto from 'crypto' import { transformInfoFromDBToJson } from './utils/transformationUtils' import zlib from 'zlib' export const handler = async (event: APIGatewayProxyEvent, context: any) => { try { // 获取原始数据并转成JSON字符串 const rawData = transformInfoFromDBToJson(await getInfoFromDB()) const responseBody = JSON.stringify(rawData) // 生成基于原始内容的ETag(保证缓存一致性) const freshETag = crypto.createHash('md5').update(responseBody).digest('hex') const MAX_AGE = process.env.CACHE_MAX_AGE const cacheControl = `max-age=${MAX_AGE}, must-revalidate` const headers: { [key: string]: string } = { ETag: freshETag, 'Cache-Control': cacheControl, 'Content-Type': 'application/json', // 明确内容类型,帮助客户端解析 } let isCompressed = false let compressedBody: Buffer | undefined // 仅在开启压缩且客户端支持时执行压缩 if (process.env.COMPRESS_RESPONSE === 'true') { const acceptEncoding = event.headers['Accept-Encoding'] || event.headers['accept-encoding'] || '' // 优先选择客户端支持的压缩格式 if (acceptEncoding.includes('gzip')) { compressedBody = zlib.gzipSync(responseBody) headers['Content-Encoding'] = 'gzip' isCompressed = true } else if (acceptEncoding.includes('deflate')) { compressedBody = zlib.deflateSync(responseBody) headers['Content-Encoding'] = 'deflate' isCompressed = true } } // 返回对应内容:压缩后的二进制或原始字符串 return { statusCode: 200, body: isCompressed ? compressedBody!.toString('base64') : responseBody, headers, isBase64Encoded: isCompressed, // 关键:告知API Gateway这是Base64编码的二进制数据 } } catch (error) { console.error('Error fetching info:', error) return { statusCode: 500, body: JSON.stringify({ message: 'Error fetching info: Internal Server Error' }), headers: { 'Content-Type': 'application/json' }, } } }
关键修改说明
- 添加
Content-Type头:明确告诉客户端返回的是JSON格式,避免解析歧义 - 动态判断压缩格式:根据客户端的
Accept-Encoding头选择合适的压缩算法,兼容性更好 - 设置
isBase64Encoded:这是让API Gateway正确传递二进制压缩数据的核心,客户端收到后会自动解码 - ETag基于原始内容:不管是否压缩,ETag都用原始数据生成,确保缓存系统能正确识别内容是否变化
内容的提问来源于stack exchange,提问作者Allanh
相关产品推荐
相关产品推荐

