Localstack-AWS API Gateway使用Terraform启用二进制支持

问题描述

我无法在本地堆栈上使用带有terraform的API网关来启用二进制支持

该示例很简单:Lambda生成类似于Github的化身,并将图像返回为PNG。

Lambda(代理)与API网关集成(想法是:GET /avatars/{username}-> image / png)

当我调用发布的URL时(我正在Localstack上执行此操作),API始终返回Base64编码的图像,而不应用CONVERT_TO_BINARY

这是基本步骤...

  1. 创建ReST API:
resource "aws_api_gateway_rest_api" "api" {
  name = var.api_name
  
  # enable support for 'image/png'
  binary_media_types = [
    "image/png",]
}
  1. 创建GET /avatars/{username}端点:
# ReST API endpoint 'avatars'
resource "aws_api_gateway_resource" "avatars" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  parent_id   = aws_api_gateway_rest_api.api.root_resource_id
  path_part   = "avatars"
}

# 'avatars' endpoint resource path parameter.
resource "aws_api_gateway_resource" "resource" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  parent_id   = aws_api_gateway_resource.avatars.id
  path_part   = "{username}"
}

# Defines the resource HTTP method (verb or action).
resource "aws_api_gateway_method" "get-avatar" {
  rest_api_id   = aws_api_gateway_rest_api.api.id
  resource_id   = aws_api_gateway_resource.resource.id
  http_method   = "GET"
  authorization = "NONE"

  request_parameters = {
    "method.request.path.username" = true
  }
}
  1. GET /avatars/{username}端点集成到ReST API中:
resource "aws_api_gateway_integration" "resource_integration" {
  rest_api_id             = aws_api_gateway_rest_api.api.id
  resource_id             = aws_api_gateway_resource.resource.id
  http_method             = aws_api_gateway_method.get-avatar.http_method
  type                    = "AWS_PROXY"
  integration_http_method = "POST"
  uri                     = aws_lambda_function.lambda.invoke_arn
  passthrough_behavior    = "WHEN_NO_MATCH"

  request_parameters = {
    "integration.request.path.id" = "method.request.path.username"
  }
}
resource "aws_api_gateway_integration_response" "get-avatar-response" {
  rest_api_id      = aws_api_gateway_rest_api.api.id
  resource_id      = aws_api_gateway_resource.resource.id
  http_method      = aws_api_gateway_method.get-avatar.http_method
  status_code      = "200"
  content_handling = "CONVERT_TO_BINARY"
}
  1. 部署ReST API:
resource "aws_api_gateway_deployment" "deployment" {
  rest_api_id = aws_api_gateway_rest_api.api.id
  stage_name  = "stage"
  depends_on  = [aws_api_gateway_method.get-avatar,aws_api_gateway_integration.resource_integration]
}

API网关假定文本数据是base64编码的字符串,并将二进制数据输出为base64编码的blob。

这里是简单的Lambda处理程序(当然,在Go很棒!:-)中)

func handler(ctx context.Context,evt events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse,error) {
    log.Printf("Processing request data for request %s.\n",evt.RequestContext.RequestID)

    username := evt.PathParameters["username"]
    if len(username) == 0 {
        code := http.StatusBadRequest
        msg := http.StatusText(code)
        return events.APIGatewayProxyResponse{Body: msg,StatusCode: code},nil
    }

    key := []byte{0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}
    icon := identicon.New7x7(key)

    log.Printf("creating identicon for '%s'\n",username)

    pngdata := icon.Render([]byte(username))
    
    body := base64.StdEncoding.EncodetoString(pngdata) // <= Base64 Encoded Response

    return events.APIGatewayProxyResponse{
        Body: body,Headers: map[string]string{
            "Content-Type": "image/png",},IsBase64Encoded: true,// <= Is Base64 Encoded? Yes!
        StatusCode:      200,nil
}

使用curl调用端点(例如):

curl 'http://localhost:4566/restapis/8nilx7bu49/stage/_user_request_/avatars/type-a-username-here'

...它以Base64编码的图像作为响应。实际上,如果我将输出通过管道传递到base64 -d以保存内容,则图像是正确的:

curl 'http://localhost:4566/restapis/8nilx7bu49/stage/_user_request_/avatars/type-a-username-here' -s | base64 -d > test.png

有人可以指出我的缺失或困惑吗?

祝一切顺利, 路卡

解决方法

我通过使用“ * / *”作为binary_media_types使其正常工作。我在某处读到,如果您指定某些内容,则客户端需要发送相同的接受内容标头。

我也没有aws_api_gateway_integration_response资源。

最后,我以ByteArray的形式返回原始图像字节。我正在使用Kotlin来实现我的lambda,但是下面的内容应该可以让您了解如何做到这一点。

@Get("/{id}/avatar")
fun getImage(id: Long): HttpResponse<ByteArray> {
    logger.info { "AvatarController.getImage($id)" }
    val image = avatarService.getImage(id)
    val bytes: ByteArray = IOUtils.toByteArray(image)
    return HttpResponse.ok(bytes).contentType(MediaType.APPLICATION_OCTET_STREAM_TYPE)

不对Base64进行编码,也不使用API​​GatewayProxyResponse。