Localstack - AWS API Gateway, обеспечивающий поддержку двоичного кода с помощью Terraform

Мне не удается включить поддержку двоичного кода с помощью шлюза API с Terraform в Localstack.

Пример прост: 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 Gateway предполагает, что текстовые данные представляют собой строку в кодировке base64, и выводит двоичные данные в виде большого двоичного объекта в кодировке base64.

Вот простой обработчик 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

Может ли кто-нибудь указать мне, что я упускаю или путаю?

Всего наилучшего, Лука


person Luca Sepe    schedule 26.10.2020    source источник
comment
У меня была такая же проблема, все исследования, которые я провел до сих пор, указывают на тот факт, что реализация шлюза API не поддерживает двоичные типы мультимедиа.   -  person Bas Tuijnman    schedule 13.11.2020


Ответы (1)


Я заставил его работать, используя * / * как binary_media_types и ничего больше. Я где-то читал, что если вы укажете что-то, клиенту необходимо отправить идентичный заголовок содержимого accept.

У меня также нет ресурса aws_api_gateway_integration_response.

И, наконец, я возвращаю байты необработанного изображения в виде ByteArray. Я использую Kotlin для реализации своей лямбды, но приведенное ниже должно дать вам представление о том, как это сделать.

@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 и не используйте APIGatewayProxyResponse.

person user672009    schedule 27.11.2020