1
0

More than 1 year has passed since last update.

API Gateway + Lambdaを用いた外部APIリクエスト中継用エンドポイントの作成 メモ

Posted at
  • アプリから外部APIを呼び出す際、一度API Gateway + Lambdaの中継用エンドポイントで受けて転送したいケースがあった。

    lambda_proxy.png

  • SAMで作成してみたので、メモとして残しておく。

事前準備

  • プロジェクト作成

    sam init
    

    ※RuntimeにはPython 3.8を選択し、テンプレートには、Hello Worldテンプレートを使用。

コード

  • swagger.json

    • API定義
    {
       "openapi": "3.0.0",
       "info": {
          "version": "2016-09-12T17:50:37Z",
          "title": "ProxyIntegrationWithLambda"
       },
       "paths": {
          "/{proxy+}": {
             "x-amazon-apigateway-any-method": {
                "parameters": [
                   {
                      "name": "proxy",
                      "in": "path",
                      "required": true,
                      "schema": {
                         "type": "string"
                      }
                   }
                ],
                "responses": {},
                "x-amazon-apigateway-integration": {
                   "responses": {
                      "default": {
                         "statusCode": "200"
                      }
                   },
                   "uri": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${BypassFunction.Arn}/invocations",
                   "passthroughBehavior": "when_no_match",
                   "type": "aws_proxy"
                }
             }
          }
       }
    }
    
  • template.yaml

    AWSTemplateFormatVersion: '2010-09-09'
    Transform: 'AWS::Serverless-2016-10-31'
    
    Globals:
      Api:
        EndpointConfiguration: REGIONAL
        Cors: "'*'"
    
    Resources:
      BypassFunction:
        Type: AWS::Serverless::Function
        Properties:
          Handler: app.lambda_handler
          Runtime: python3.8
          CodeUri: ./hello_world/
          Events:
            Callback:
              Type: Api
              Properties:
                Path: /proxy/{proxy+}
                Method: any
                RestApiId:
                  Ref: BypassAPI
      BypassAPI:
        Type: AWS::Serverless::Api
        Properties:
          StageName: Dev
          DefinitionUri: ./swagger.json
    
  • app.py

    • http://localhost:3000/proxy/oauth2/v3/certsのようにリクエストを受け取り、proxy以降のパス部とベースパスを連結して、外部APIにリクエストを投げる。

    • 今回の場合はgoogleのjwksエンドポイントで試した。

    import json
    import urllib.request
    base_path = "https://www.googleapis.com/"
    
    def lambda_handler(event, context):
        req_url = base_path+event["pathParameters"]["proxy"]
        req_headers = event["headers"]
        req_body = event["body"]
        if event["body"] != None:
            req = urllib.request.Request(req_url, json.dumps(req_body).encode(), headers=req_headers, method=event["requestContext"]["httpMethod"])
        else:
            req = urllib.request.Request(req_url)
        body=""
        headers=""
        code=200
        with urllib.request.urlopen(req) as res:
            body = json.load(res)
            headers = dict(res.getheaders())
            code = res.getcode()
        response = {
                'statusCode': code,
                'body': body
        }
        #Content-LengthとTransfer-Encodingが共存することになりエラーになったため、コメントアウト...
        #response["headers"] = headers
        return response
    

動作確認

  • ビルド

    sam build
    
  • 起動

    sam local start-api
    
  • リクエスト

    GET /proxy/oauth2/v3/certs HTTP/1.1
    Host: localhost:3000
    
  • レスポンス

    {'keys': [
            {'kid': '7483a088d4ffc00609f0e22f3c22da5fe3906ccc', 'kty': 'RSA', 'n': 'yF2BwlYB66C_-FKSk4riKizNKGjTyi2LYz_MImk95lPcW3SDpheJRghc_5iaZxpf1xAMDwHphdkEPal_kNnqLu49B7XGv9iPRUgzAEBD-w9E1CjrxzCRhM8MRVgUo7Msnydd6wOqNDKFCD1TlKpkGrUS32PB1jsu-OuwG6HCL1cxSNzP7SCDgCKecPvnpwcNarc3FzzJDraUaPX639cvvBf5PDeltBzkAIbSY0YS6RVBmZpKEDO_C1GtbADqvpz95uSdYO6H4tjUQtFwooNsAL8tv1TmLSehClFyYzaESOBfSCpxudNmPgBEzfAZM_WoZ2JukAo73Ynu3YxJOJtMTQ', 'alg': 'RS256', 'use': 'sig', 'e': 'AQAB'
            },
            {'n': '9rWgi2qEYWWMfkF7U947MGGZBx6pNVCUi9tgTKqmo-8J4MaMSnu0I2-YsyLeXh5r3ztzuk65w8egHtudxE1Q47VquJIcBs4Zn95607VCJeZxYBG3CYk1OnUWGuTjPbpXIeAlnhc-MpgUvjphE6nMEsQsxd_PTh9rj_gUdahA0xRQBj-8kOhnOoFQAO0ZV5mP5gyhTBZubQOIeKVvjZJvxZqSoSWqocndVGrVEAYMdjXbxdw8Z9Gf0NiWk3rImEjzXu65a62cJ6Fn1PjQEhohvwM0cCvKS6kq7bZ3ABJ__9VQtC6SyN0jo_jxAIVfWyVmZG-o-JIClrxjeMlRkqbbZQ', 'alg': 'RS256', 'e': 'AQAB', 'kid': '580adb0c32a175d950a1c1901c182fc17341ddc4', 'kty': 'RSA', 'use': 'sig'
            }
        ]
    }
    

参考情報

1
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
1
0