リダイレクトするAPI GatewayとLambdaを作ってみた

WebサイトのバックエンドのAPIで「リダイレクトしたい」場合に、どうすればよいか試してみました。
2021.12.01

WebサイトのバックエンドのAPIを作っているとき、次の動作を実現したくなりました。

  • リダイレクトしたい
  • 条件によって、リダイレクト先のURLを変えたい

単純なリダイレクトであれば、API Gateway単体でも実現可能だと思いますが、DynamoDBのデータ等でリダイレクト先のURLを変更したいのです。 そこで、Lambdaを使って実際に試してみました。

おすすめの方

  • AWS SAMでAPIを作りたい方
  • AWS SAMでLambdaを作りたい方
  • API GatewayとLambdaでリダイレクトしたい方(Lambdaプロキシ統合の場合)

リダイレクトするAPIを作る

sam init

sam init \
    --runtime python3.9 \
    --name Redirect-Api-Sample \
    --app-template hello-world \
    --package-type Zip

SAMテンプレート

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Redirect-Api-Sample

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Timeout: 5
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get

  HelloWorldFunctionLogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}

Outputs:
  HelloWorldApi:
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"

Lambdaコード

HTTPステータスを303にして、Locationヘッダーを付与しているだけです。

app.py

def lambda_handler(event, context):
    # ここでなにか処理をして、その結果によってLocationを変更する、など
    return {
        "statusCode": 303,
        "headers": {
            "Location": "https://dev.classmethod.jp/"
        }
    }

デプロイ

sam build

sam package \
    --output-template-file packaged.yaml \
    --s3-bucket cm-fujii.genki-deploy

sam deploy \
    --template-file packaged.yaml \
    --stack-name Redirect-Api-Sample-Stack \
    --s3-bucket cm-fujii.genki-deploy \
    --capabilities CAPABILITY_NAMED_IAM \
    --no-fail-on-empty-changeset

APIエンドポイント(URL)の取得

aws cloudformation describe-stacks \
    --stack-name Redirect-Api-Sample-Stack \
    --query 'Stacks[].Outputs'

動作確認

取得したURLにブラウザでアクセスすると、DevelopersIOにリダイレクトされました。 curlコマンドで確認すると、HTTPステータスとLocationヘッダーがありました。

$ curl -D - https://xxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
HTTP/2 303 
content-type: application/json
content-length: 0
location: https://dev.classmethod.jp/
略

Lambdaエラーの場合

Lambdaでエラー発生したことを想定して、動作確認してみます。

app.py

def lambda_handler(event, context):
    raise NotImplementedError()

この場合は、Internal Server Errorのメッセージが返ってきます。

Internal Server Error

curlコマンドで確認すると、HTTPステータスは502です。

$ curl -D - https://xxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/ 
HTTP/2 502 
content-type: application/json
content-length: 36
略

{"message": "Internal server error"}

これは、API GatewayをLambdaを「Lambdaプロキシ統合」で利用しているためです。 「Lambdaプロキシ統合」では、LambdaがAPI Gatewayに対して、想定されたResponseを返す必要があります。 しかし、Lambda自体がエラーになった場合は、想定されたResponseをAPI Gatewayに返せないため、API Gateway側で502が発生しています。

関数出力が別の形式である場合、API Gateway は 502 Bad Gateway エラーレスポンスを返します。

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format

参考