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)