- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2019-01-10T05:59:13+00:00","","")
[[AWSメモ]] >l;
* AWS API Gateway&LambdaをFlask的に移行しやすいように作る [#ibb8bd56]
#author("2019-01-10T06:35:58+00:00","","")
[[AWSメモ]] >
* AWS API Gateway&LambdaをFlaskに移行しやすいように作る [#ibb8bd56]
#setlinebreak(on);
#contents
-- 関連
--- [[ChaliceでAPI Gateway&Lambda開発]]
-- 参考
--- [[https://github.com/aws/chalice]]
** 概要 [#nca4a038]
#html(<div style="padding-left: 10px;">)
素の API Gateway & Lambda が、仮にサーバを立てて運用するように変わっても移行しやすい形を考えてみた。
※EC2 & flask + uwsgi + nginx に移行する場合とか。
以下、Flask に移行するケースを考えてみた。
※というか、最初から [[chalice:https://github.com/aws/chalice]] で作っていれば、殆ど手を入れる事なく移行できると思う。
#html(</div>)
** メインハンドラ [#g6afcfa1]
#html(<div style="padding-left: 10px;">)
app.py
#mycode2(){{
# TODO: ビジネスロジックは、Lambdaのコンテキストに依存しない形で別ファイルに外出しする
from myframework import WebApp
app = WebApp()
@app.route('/books', methods=['GET'])
def book_list(event, context):
return {'body': f'books_list'}
@app.route('/books', methods=['POST'])
def book_create(event, context):
return {'body': f'book_create'}
@app.route('/books/{id}', methods=['PUT'])
def book_update(event, context, id):
return {'body': f'book_update : {id}'}
@app.route('/books/{id}', methods=['DELETE'])
def book_update(event, context, id):
return {'body': f'book_delete : {id}'}
}}}
他のインターフェース例(サンプルなので book_update だけ)
#mycode2(){{
# (1) コンテキスト依存を無くしたい場合(リクエストデータ、リクエストヘッダ)
@app.route('/books/{id}', methods=['PUT'])
def book_update(data, headers):
return {'body': f'book_update : {data["id"]}'}
# (2) コンテキスト依存を無くしたい場合(ラッパーリクエストで受渡し)
@app.route('/books/{id}', methods=['PUT'])
def book_update(request):
return {'body': f'book_update : {request["path_params"]["id"]}'}
# (3) flask、chalice的
@app.route('/books/{id}', methods=['PUT'])
def book_update(id):
return {'body': f'book_update : {id}\n'}
# (4) Lambdaハンドラのインターフェースそのままにする場合
def book_update(event, context):
return {'body': f'book_update : {id}\n'}
}}
#html(</div>)
#html(</div>)
** ラッパー [#qdc0ed93]
#html(<div style="padding-left: 10px;">)
myframework.py
#mycode2(){{
import json
class WebApp(object):
def __init__(self):
self.path_infos = []
def route(self, path, methods=[]):
def d_wrapper(f):
l_methods = [x.lower() for x in methods]
self.path_infos.append({'f': f, 'path': path, 'methods': l_methods})
def f_wrapper(*args, **kwargs):
print(f'decorator path : {path}')
print(f'decorator methods : {l_methods}')
print(f'function args : {args}')
print(f'function kwargs : {kwargs}')
f(*args, **kwargs)
return f_wrapper
return d_wrapper
class WebRequest(object):
def __init__(self, event):
self.http_method = event.get('httpMethod') if event.get('httpMethod') else ''
self.path_params = event.get('pathParameters') if event.get('pathParameters') else {}
self.query_params = event.get('queryStringParameters') if event.get('queryStringParameters') else {}
self.headers = event.get('headers') if event.get('headers') else {}
def to_dict():
return {
'http_method': self.http_method,
'path_params': self.path_params,
'query_params': self.query_params,
'headers': self.headers
}
def handler(event, context):
"""全Lambdaで共通のメインハンドラ."""
print('main handler')
# アプリケーションをインポート
from app import app
print(event)
# リクエスト情報の取得
resource = event.get('resource') if event.get('resource') else '/'
http_method = event.get('httpMethod') if event.get('httpMethod') else ''
path_params = event.get('pathParameters') if event.get('pathParameters') else {}
query_params = event.get('queryStringParameters') if event.get('queryStringParameters') else {}
# 対象関数の判定
target_function = None
for info in app.path_infos:
check_path = info['path']
for name, val in path_params.items():
check_path = check_path.replace('{'+name+'}', val)
if check_path == resource and http_method.lower() in info['methods']:
target_function = info['f']
# リクエストデータを纏める(パスパラメータ、GETパラメータ、POSTデータを1つの辞書に纏める)
#json_text = event.get('body') if event.get('body') else '{}'
#json_data = json.loads(json_text)
#request_data = {**path_params, **query_params, **json_data}
#request_headers = event['headers']
# 対象関数の実行
if target_function:
return target_function(event, context, **path_params) # event, context, パスパラメータ
#return target_function(request_data, request_headers) # (1) コンテキスト依存を無くしたい場合(リクエストデータ、リクエストヘッダ)
#return target_function(WebRequest(event)) # (2) コンテキスト依存を無くしたい場合(ラッパーリクエストで受渡し)
#return target_function(**path_params) # (3) flask、chalice的
#return target_function(event, context) # (4) Lambdaハンドラのインターフェースそのままにする場合
else:
return {'statusCode': 404} # 403?
}}
#html(</div>)
** template.yml [#t81396c9]
#html(<div style="padding-left: 10px;">)
#mycode2(){{
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Test serverless application.
Resources:
BookFunction:
Type: AWS::Serverless::Function
Properties:
Handler: myframework.handler
Runtime: python3.6
Events:
List:
Type: Api
Properties:
Path: /books
Method: get
Create:
Type: Api
Properties:
Path: /books
Method: post
Update:
Type: Api
Properties:
Path: /books/{id}
Method: put
Delete:
Type: Api
Properties:
Path: /books/{id}
Method: delete
}}
#html(</div>)