LoginSignup
5
0

More than 1 year has passed since last update.

Amazon API Gatewayで地図データを配信するバックエンドをCDKで作る。

Last updated at Posted at 2022-12-11

AwsGatewayで地図データ(PNG)を配信するAPIを作成し、S3に保存されたpngファイルを画像データとしてApiGatewayを使って配信したい。

これは MIERUNE AdventCalendar 2022 13日目の記事です。
昨日は @chizutodesign さんによる MapTilerの地図アイコンをデザインした話 でした。

APIのURLは以下を想定。ApiGateway+Lambdaで構築する。

(汎用例)https://hogehogeapi/raster/<バケット名>?key=<S3オブジェクトパス>.png
(具体例)https://hogehogeapi/raster/s3backetname?key=/10/20/30.png

実装方針

S3バケット名をパスパラメータに、オブジェクトパスをkey(クエリパラメータ)としてLambda関数に渡し、Lambda関数はS3よりオブジェクトを取得してBASE64にエンコードしApiGatewayに返却、ApiGatewayはBASE64をデコードしてクライアントへ返却する。
Lambdaに使用する言語はPythonで構築、以下のようなLambda関数を用意する。 (raster.py)
BASE64にエンコード後にApiGatewayでバイナリに戻して上げる点が注意点となる。

# raster.py
import base64
from pathlib import Path
import boto3

s3 = boto3.resource("s3")
def get_raster(event, context):
    try:
        bucket = s3.Bucket(event["bucket"])
        path = event["path"]
        if Path(path).suffix == ".png":
            response = bucket.Object(path).get()
            data = response["Body"].read()
            return base64.b64encode(data).decode("utf-8")
        else:
            return "Invalid path"
    except Exception as e:
        print(f"Exception..{e}")
        return "Invalid Request"

AWSコンソールからのApiGatewayの設定

APIGatewayにおいてLambdaプロキシ統合の使用を選択すると、文字列(主にJSONなど)を返却するようになるため、バイナリで返却したい場合以下の設定をApiGatewayで行う必要がある。

  • バイナリタイプとして扱うメディアタイプを指定して、APIのバイナリサポートを設定有効にする。
    スクリーンショット 2022-11-30 14.51.22.png

  • メソッドリクエストの設定にURLクエリ文字列パラメータを設定する。
    スクリーンショット 2022-11-30 14.52.57.png

  • 統合レスポンスの設定を行いたいため、統合リクエストのLambdaプロキシ統合の使用のチェックを外す。
    上記の設定によりLambdaにわたすリクエストのマッピングは自動で設定されないので、マッピングテンプレートを設定する。
    スクリーンショット 2022-11-30 14.53.55.png

  • 統合レスポンスの設定。
    コンテンツの処理の設定を行うとApiGatewayはLambda関数から渡されたレスポンス(BASE64)を統合レスポンスでバイナリ化してメソッドレスポンスとしてクライアント側へ返却する。
    スクリーンショット 2022-11-30 14.54.56.png

  • メソッドレスポンスの設定。
    スクリーンショット 2022-11-30 14.55.41.png

上記の設定を行うことにより、https://hogehogeapi/raster/<バケット名>?key=.pngでこのAPIを呼び出すと、S3に保存されている /<バケット名>/< S3オブジェクトパス>.png がクライアント側にコンテンツタイプ
image/png として呼び出され、PNG画像が表示される。

CDKでの実装例

AWSコンソール画面からの設定では毎回同じことを繰り返さなければならないのでCDKで作成しておく。
以下がその実装例。

肝となる点は以下

    const lambdaIntegration = new LambdaIntegration(
        .
        .
        .
        proxy: false,
        passthroughBehavior: PassthroughBehavior.WHEN_NO_MATCH,
        requestTemplates: { 'application/json': requestTemplates },
        integrationResponses: [
          {
            statusCode: '200',
            contentHandling: ContentHandling.CONVERT_TO_BINARY,
            responseParameters: { 'method.response.header.Content-Type': "'image/png'" }
          },
        ]

全てのコード

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { ContentHandling, LambdaIntegration, PassthroughBehavior, RestApi } from 'aws-cdk-lib/aws-apigateway';

export class hogehogeapi extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const requestTemplates = `
      {
        "bucket": "$input.params(\'bucket\')", 
        "key": "$input.params(\'key\')",
      }`;

    const lambdaIntegration = new LambdaIntegration(
      new lambda.Function(
        this,
        `get_raster`,
        {
          functionName: "funcName",
          runtime: lambda.Runtime.PYTHON_3_8,
          code: lambda.Code.fromAsset('./handler'),
          memorySize: 128,
          handler: "handlerName"
        }),
      {
        proxy: false,
        passthroughBehavior: PassthroughBehavior.WHEN_NO_MATCH,
        requestTemplates: { 'application/json': requestTemplates },
        integrationResponses: [
          {
            statusCode: '200',
            contentHandling: ContentHandling.CONVERT_TO_BINARY,
            responseParameters: { 'method.response.header.Content-Type': "'image/png'" }
          },
        ]
      }
    );

    // APIGateway関連の設定
    const api = new RestApi(this, `api`, {
      restApiName: `hogehogeapi`,
      description: `hogehogeapi backend Service`,
      binaryMediaTypes: ['image/png'],
      deployOptions: {
        stageName: 'dev'
      },
    });

    const CorsOptions = {
      statusCode: 200,
      allowOrigins: ['*'],
      allowMethods: ['OPTIONS', 'GET']
    };

    const resource = api.root.addResource('raster')
    const resourcePath = resource.addResource('{bucket}');
    resourcePath.addMethod('GET', lambdaIntegration, {
      requestParameters: {
        'method.request.querystring.key': false,
      },
      methodResponses: [{
        statusCode: '200',
        responseParameters: {
          'method.response.header.Content-Type': true
        },
        responseModels: {
          'image/png': { modelId: 'Empty' }
        }
      }]
    });
    resourcePath.addCorsPreflight(CorsOptions);
  }
}

明日は @dayjournal さんによる QGISのプロセッシングを色々とためしてみたです!お楽しみにー

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0