template.ymlは下記とした
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > customdomain-dynamodb-app Customdomain API-gateway and DynamoDB my Example Parameters: DomainName: Type: String Default: api.my.example.com HostedZoneName: Type: String Default: my.example.com. # don't miss the dot at the end Globals: Api: Cors: AllowMethods: "'*'" AllowHeaders: "'*'" AllowOrigin: "'*'" Function: Timeout: 5 Environment: Variables: TABLE_NAME: !Ref TodoDyanmoTable # Lambda実行時の環境変数TABLE_NAMEで参照可能になる Resources: TodoApiGateway: Type: AWS::Serverless::Api Properties: StageName: v1 Auth: # Lambdaオーソライザーを使う # 参考DefaultAuthorizerは使えない: https://nibral.hateblo.jp/entry/2019/04/24/103036 #DefaultAuthorizer: MyLambdaTokenAuthorizer Authorizers: MyLambdaTokenAuthorizer: FunctionPayloadType: REQUEST FunctionArn: !GetAtt MyAuthFunction.Arn Identity: Headers: - Authorization ReauthorizeEvery: 1 # 認証結果を1秒間だけ結果をキャッシュする模様 APIDomainName: Type: AWS::ApiGateway::DomainName Properties: CertificateArn: arn:aws:acm:us-east-1:XXXX:certificate/XXXXXXXXX # 証明書のarn バージニア北部で作る DomainName: !Ref DomainName APIBasePathMapping: Type: AWS::ApiGateway::BasePathMapping Properties: DomainName: !Ref APIDomainName RestApiId: !Ref TodoApiGateway Stage: v1 HelloTodoFunction: # GET用 Type: AWS::Serverless::Function Properties: CodeUri: hello_todo/ Handler: app.get_lambda_handler Runtime: python3.7 Policies: - DynamoDBCrudPolicy: TableName: !Ref TodoDyanmoTable Events: HelloTodo: Type: Api Properties: Path: /todo Method: get Auth: Authorizers: MyLambdaTokenAuthorizer RestApiId: Ref: TodoApiGateway POSTTodoFunction: # POST用 Type: AWS::Serverless::Function Properties: CodeUri: hello_todo/ Handler: app.post_lambda_handler Runtime: python3.7 Policies: - DynamoDBCrudPolicy: TableName: !Ref TodoDyanmoTable Events: HelloTodo: Type: Api Properties: Path: /todo Method: post Auth: Authorizers: MyLambdaTokenAuthorizer RestApiId: Ref: TodoApiGateway TodoDyanmoTable: Type: AWS::DynamoDB::Table Properties: BillingMode: PROVISIONED # 無料枠はこっち AttributeDefinitions: - AttributeName: "user" # 属性定義 AttributeType: "S" # 文字列 - AttributeName: "id" AttributeType: "N" # 数値 KeySchema: - AttributeName: "user" KeyType: "HASH" # ハッシュキー - AttributeName: "id" KeyType: "RANGE" # レンジキー ProvisionedThroughput: ReadCapacityUnits: 2 WriteCapacityUnits: 2 MyAuthFunction: # Token認証用lambda Type: AWS::Serverless::Function Properties: CodeUri: hello_todo/ Handler: app.auth_request_lambda_handler Runtime: python3.7 Outputs: HelloTodoFunction: Description: "Hello World Lambda Function ARN" Value: !GetAtt HelloTodoFunction.Arn
参考にしたサイト↓ではroute53の設定も入っていたが上手く動作しなかったため手動route53をで設定した。
https://github.com/awslabs/serverless-application-model/issues/40
route53の設定は下記を参照(route53にはAレコードでエリアス、cloudfront.netで終わるものを設定する)
https://www.beex-inc.com/blog/apigateway-using-custom-domain/
app.pyは下記とした。
import os import boto3 from boto3.dynamodb.conditions import Key, Attr import json import decimal import logging logger = logging.getLogger() logger.setLevel(logging.DEBUG) # import requests def auth_request_lambda_handler(event, context): # 参考: https://dev.classmethod.jp/server-side/serverless/lambda-authorizer/ logger.info('## ENVIRONMENT VARIABLES') logger.info(os.environ) logger.info('## EVENT') logger.info(event) token = event["headers"]["Authorization"] logger.info('## token='+str(token) ) if token == "1": logger.info('## sucess' ) myaddisonalinfo = { "id": 1, "hoge": "foo" } tmp_str = json.dumps(myaddisonalinfo) base64_context = base64.b64encode(tmp_str.encode('utf8')) return { "principalId" : token, "policyDocument" : { "Version" : "2012-10-17", "Statement" : [ { "Action": "*", "Effect": "Allow", "Resource": "arn:aws:execute-api:*:*:*/*" } ] }, 'context': { 'additional_info': base64_context.decode('utf-8') } } # 1以外の時はNG return { "principalId" : 2, "policyDocument" : { "Version" : "2012-10-17", "Statement" : [ { "Action": "*", "Effect": "Deny", "Resource": "arn:aws:execute-api:*:*:*/*" } ] } } def decimal_default_int(obj): """ json.dumps用 https://qiita.com/ekzemplaro/items/5fa8900212252ab554a3 https://forums.aws.amazon.com/message.jspa?messageID=689708 """ if isinstance(obj, decimal.Decimal): return int(obj) raise TypeError def getTodos(user:str, dynamo): """ 指定されたユーザのTodosを取得 """ tmp_q = dynamo.query( KeyConditionExpression = Key('user').eq(user) ) # queryした結果のItemsに本当の結果が入っている res = tmp_q["Items"] return res def putTodos(body_dict): dynamo = boto3.resource('dynamodb').Table(os.environ['TABLE_NAME']) item = { "user": body_dict["user"], "id": body_dict["id"], "todo": body_dict["todo"] } dynamo.put_item(Item = item) def post_lambda_handler(event, context): """ curl -X POST -H 'Content-Type:application/json' -d '{ "user": "foo", "id": 2, "todo": "aaabbb" }' "https://api.my.example.com/todo" -H 'Authorization:1' """ logger.info('## ENVIRONMENT VARIABLES') logger.info(os.environ) logger.info('## EVENT') logger.info(event) # eventのbodyがPOSTしたパラメータ body = event['body'] logger.info('## BODY') logger.info(body) logger.info('## json.loads') body_dict = json.loads(body) logger.info('## putTodos()') putTodos(body_dict) ret_dict = { "statusCode": 200, "body": '{ "result": "ok" }', "headers": { "Access-Control-Allow-Origin": "*" } } return ret_dict def get_lambda_handler(event, context): logger.info('## ENVIRONMENT VARIABLES') logger.info(os.environ) logger.info('## EVENT') logger.info(event) #logger.info('## CONTEXT') #logger.info(context) # eventのqueryStringParametersがGETしたパラメータ q_string_p = event["queryStringParameters"] # 想定文字列 # curl "https://api.my.example.com/todo?user=foo" -H 'Authorization:1' taget_user = q_string_p["user"] dynamo = boto3.resource('dynamodb').Table(os.environ['TABLE_NAME']) res = getTodos(taget_user, dynamo) ret_dict = { "statusCode": 200, "body": json.dumps({"todos": res}, default=decimal_default_int),
"headers": { "Access-Control-Allow-Origin": "*" } } return ret_dict if __name__ == '__main__': print("-- sart --") dynamo = boto3.resource('dynamodb').Table(os.environ['TABLE_NAME']) res = getTodos("foo", dynamo) print(res) print("---") ret_dict = { "statusCode": 200, "body": json.dumps({"todos": res}, default=decimal_default_int) } print(ret_dict)