Angular AWS 无服务器 lambda 上的 SSR returns 随机数和字符

Angular SSR on AWS serverless lambda returns random numbers and characters

我正在尝试使用无服务器和 lambda 将 Angular SSR 应用程序部署到 AWS。作为这方面的初学者,我正在使用 this starter project to do so. I did not change any of the code except changing the name of the project to "public". There is a dedicated section in the readme 来解释如何做到这一点(“查找并用您的项目名称替换单词“ngx-serverless-starter””)。

之后我将项目上传到AWS。这是日志:

Serverless: Stack update finished...
Service Information
service: public
stage: dev
region: us-east-1
stack: public-dev
resources: 12
api keys:
  None
endpoints:
  GET - https://u3vc0nd65k.execute-api.us-east-1.amazonaws.com/dev
  GET - https://u3vc0nd65k.execute-api.us-east-1.amazonaws.com/dev/{proxy+}
functions:
  api: public-dev-api
layers:
  None

所以我假设它有效。如果我点击提供的 URL 虽然我看到的是随机数字和字符(由于 Whosebug 的字符限制,我不得不截断它):

PCFET0NUWVBFIGh0bWw+PGh0bWwgbGFuZz0iZW4iPjxoZWFkPgogIDxtZXRhIGNoYXJzZXQ9InV0Zi04Ij4KICA8dGl0bGU+Tmd4U2VydmVybGVzc1N0YXJ0ZXI8L3RpdGxlPgogIDxiYXNlIGhyZWY9Ii8iPgogIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MSI+CiAgPGxpbmsgcmVsPSJpY29uIiB0eXBlPSJpbWFnZS94LWljb24iIGhyZWY9ImZhdmljb24uaWNvIj4KPGxpbmsgcmVsPSJzdHlsZXNo

在我看来,似乎返回了一些错误的内容类型,但我无法真正理解它。

随机字符串是什么?

“随机数字和字符”实际上不是随机的,而是HTML页面的base64编码。如果您对响应进行 base64 解码,您将获得一个 HTML 页面。

为什么我得到的是 base64 编码?

module.exports.handler = serverlessExpress({
  app,
  binarySettings: {
    isBinary: ({ headers }) => true,
    contentTypes: [],
    contentEncodings: [],
  },
});

base64编码是由于binarySettings in lambda.js. From the binarySettings section in serverless-express README:

binarySettings

Determine if the response should be base64 encoded before being returned to the event source, for example, when returning images or compressed files. This is necessary due to API Gateway and other event sources not being capable of handling binary responses directly. The event source is then responsible for turning this back into a binary format before being returned to the client.

By default, this is determined based on the content-encoding and content-type headers returned by your application. If you need additional control over this, you can specify binarySettings.

{   
  binarySettings: {
    isBinary: ({ headers }) => true,
    contentTypes: ['image/*'],
    contentEncodings: []   
  }
}

Any value you provide here should also be specified on API Gateway API. In SAM, this looks like:

ExpressApi:
  Type: AWS::Serverless::Api
  Properties:
    StageName: prod
    BinaryMediaTypes: ['image/*']

为什么会有启用 base64 编码的选项?

base64 编码选项主要用于您通过 AWS API 网关发送二进制数据作为响应的场景(例如:图像、GZip 文件等)。为了使二进制内容正常工作,您需要以 base64 编码格式发送 AWS Lambda proxy integration:

的响应

To handle binary payloads for AWS Lambda proxy integrations, you must base64-encode your function's response. You must also configure the binaryMediaTypes for your API. Your API's binaryMediaTypes configuration is a list of content types that your API treats as binary data. Example binary media types include image/png or application/octet-stream. You can use the wildcard character (*) to cover multiple media types. For example, */* includes all content types.

修复 Content-Type header(可选)

默认情况下,页面响应的 Content-Type header 为 application/json。您可以在 server.tsserverless.ts 中将其更改为 text/html。这并不是解决问题所必需的,但最好配置正确的 Content-Type.

  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    res.header('Content-Type', 'text/html');
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

选项 1:禁用响应的 base64 编码

lambda.js 中注释掉 binarySettings 以便响应不是 base64 编码的。

module.exports.handler = serverlessExpress({
  app,
  // binarySettings: {
  //   isBinary: ({ headers }) => true,
  //   contentTypes: [],
  //   contentEncodings: [],
  // },
});

选项 2:使用 API 网关转换 base64 响应

如果您在API 网关中设置binaryMediaTypes,它会自动将base64 编码转换为正确的响应。如果您没有在可选步骤中更改 Content-Type,请使用 application/json

provider:
  name: aws
  runtime: nodejs12.x
  lambdaHashingVersion: 20201221
  memorySize: 192
  timeout: 10
  apiGateway:
    binaryMediaTypes:
      - 'text/html'

我创建了一个 PR 禁用 base64 编码(选项 1)。

注意:我必须在浏览器中进行硬刷新才能在新部署后更正响应。

接受的答案似乎解决了我的问题,但结果是图像没有渲染。奇怪的是没有错误(状态代码 200),但他们不会出现。

This thread 引导我走向正确的方向:

我在 lambda.js:

中再次添加了二进制设置
module.exports.handler = serverlessExpress({
  app,
  binarySettings: {
      isBinary: ({ headers }) => true,
      contentTypes: [],
      contentEncodings: [],
   },
});

之后我更改了 API 网关设置并在 Binary Media Types 中添加了 */*。这成功了。