•
•
•
•
•
•
•
•
•
•
see@	http://www.restapitutorial.com/lessons/whatisrest.html
REST	API
Client
Mobile client
REST	API
GET /users HTTP/1.1
{"users": […]}
REST	API
Amazon
DynamoDB
Amazon	S3
Amazon	
CloudWatch
Auto Scaling group
Security group
Elastic Load
Balancing
Instance
REST API
Amazon
DynamoDB
Amazon	
CloudWatch
Amazon	S3
Amazon
DynamoDB
Amazon	
CloudWatch
Amazon API Gateway
AWS Lambda
Amazon	S3
Lambda.GetFunction(params: {'body': '', 'url': u'https://lambda.us-west-2.amazonaws.com/2015-03-31
IAM.GetRole(params: {'body': {'Action': u'GetRole', 'RoleName': u'helloworld7', 'Version': u'2010-
IAM.CreateRole(params: {'body': {'Action': u'CreateRole', 'RoleName': u'helloworld7', 'Version': u
IAM.PutRolePolicy(params: {'body': {'Action': u'PutRolePolicy', 'RoleName': u'helloworld7', 'Polic
Lambda.CreateFunction(params: (... omitted from logs due to size ...)
APIGateway.GetRestApis(params: {'body': '', 'url': u'https://apigateway.us-west-2.amazonaws.com/re
APIGateway.CreateRestApi(params: {'body': '{"name": "helloworld7"}', 'url': u'https://apigateway.u
APIGateway.GetResources(params: {'body': '', 'url': u'https://apigateway.us-west-2.amazonaws.com/r
APIGateway.PutMethod(params: {'body': '{"authorizationType": "NONE"}', 'url': u'https://apigateway
APIGateway.PutIntegration(params: {'body': '{"httpMethod": "POST", "requestTemplates": {"applicati
APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}',
APIGateway.PutIntegrationResponse(params: {'body': '{"responseTemplates": {"application/json": ""}
APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}',
APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "ChaliceViewError.*", "re
APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}',
APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "BadRequestError.*", "res
APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}',
APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "NotFoundError.*", "respo
APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}',
APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "UnauthorizedError.*", "r
APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}',
APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "ForbiddenError.*", "resp
APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}',
APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "ConflictError.*", "respo
APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}',
APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "TooManyRequestsError.*",
Lambda.GetPolicy(params: {'body': '', 'url': u'https://lambda.us-west-2.amazonaws.com/2015-03-31/f
Lambda.AddPermission(params: {'body': '{"Action": "lambda:InvokeFunction", "StatementId": "1e96468
APIGatewayCreateDeployment(params: {'body': '{"stageName": "dev"}', 'url': u'https://apigateway.us
API
# AWS SAM
https://github.com/aws/chalice
•
•
•
•
•
•
•
$ pip install chalice
$ chalice new-project helloworld && cd helloworld
$ cat app.py
from chalice import Chalice
app = Chalice(app_name="helloworld")
@app.route("/")
def index():
return {"hello": "world"}
$ chalice deploy
...
https://endpoint/api
$ curl https://endpoint/api
{"hello": "world"}
$ git diff
from chalice import Chalice
+import boto3
app = Chalice(app_name='chalice-sample')
+S3 = boto3.client('s3', region_name='us-east-1')
@app.route('/')
def index():
+ S3.get_object(Bucket = BUCKET, Key = key)
return {'hello': 'world'}
$ chalice deploy
Creating role: chalice-sample-dev
The following execution policy will be used:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": ["*"],
"Sid": "40e10c45a..."
},
...
}
Would you like to continue? [Y/n]:
$ chalice local
Serving on localhost:8000
$ npm install -g nodemon
$ nodemon --exec "chalice local" --watch *.py
[nodemon] 1.12.5
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: app.py
[nodemon] starting `chalice local`
Serving on localhost:8000
see@	https://qiita.com/TakenoriHirao/items/69f2af5aaf64db77124b
•
•
•
# Basic Definition
@app.route('/')
def index():
return {"GET": "/"}
# Specify Methods
@app.route('/', methods=['PUT'])
def index_put():
return {"PUT": "/"}
@app.route('/res', methods=['GET', 'POST'])
def my_resource():
request = app.current_request
if request.method == 'GET':
return {"GET": "/res"}
elif request.method == 'POST':
return {"POST": "/res"}
# Path Params
@app.route('/myresources/{object_name}')
def my_resource_key(object_name):
try:
response = S3.get_object(
Bucket = BUCKET, Key = object_name)
return response['Body'].read()
except ClientError as e:
raise e
@app.route('/s3/{bucket_name}/objects')
def objects_of(bucket_name):
try:
response =
S3.list_objects_v2(Bucket=bucket_name,
Prefix = S3_PREFIX)
objects = list(map(lambda x: x["Key"],
response["Contents"]))
return {"objects": objects}
except ClientError as e:
raise ...
@app.route('/')
def index():
return Response(
body = 'hello world!',
status_code = 200,
headers = {'Content-Type': 'text/plain'}
)
@app.route('/',
cors=True,
api_key_required=True,
authorizer=IAMAuthorizer())
def index():
return "yey"
@app.schedule(Rate(1,	unit=Rate.HOURS))
def every_hour(event):
print(event.to_dict())
•
•
•
•
•
class Chalice(object):
def route(self, path, **kwargs): #
def _register_view(view_func):
self._add_route(path, view_func, **kwargs)
return view_func
return _register_view
def _add_route(self, path, view_func, **kwargs):
methods = kwargs.pop('methods', ['GET'])
...
for method in methods:
...
entry = RouteEntry(view_func, name,
path, method, api_key_required, content_types, cors,
authorizer)
self.routes[path][method] = entry
class Chalice(object):
def __call__(self, event, context): #
resource_path = event.get('requestContext',
{}).get('resourcePath')
http_method = event['requestContext']['httpMethod']
route_entry = self.routes[resource_path][http_method]
view_function = route_entry.view_function
function_args = {name: event['pathParameters'][name]
for name in route_entry.view_args}
...
response = self._get_view_function_response(view_function,
function_args)
response_headers = CaseInsensitiveMapping(response.headers)
...
response = response.to_dict(self.api.binary_types)
return response
class Chalice(object):
def _get_view_function_response(self, view_function, function_args):
try:
response = view_function(**function_args)
if not isinstance(response, Response):
response = Response(body=response)
self._validate_response(response)
except ChaliceViewError as e:
response = Response(...)
except Exception as e:
headers = {}
if self.debug:
...
else:
response = Response(..., status_code=500)
return response
https://speakerdeck.com/akitsukada/sabaresudewang-dao-webhuremuwakuwoshi-ufang-fa
https://www.slideshare.net/shimy_net/aws-79149218
https://www.slideshare.net/shimy_net/cloud-roadshow-2017-osaka
•
•
•
https://github.com/kislyuk/domovoi
@app.sns_topic_subscriber("bartender")
def tend(event, context):
message = json.loads(event["Records"][0]["Sns"]["Message"])
context.log(dict(beer="Quadrupel", quantity=message["beer"]))
@app.cloudwatch_event_handler(source=["aws.ecs"])
def monitor_ecs_events(event, context):
message = json.loads(event["Records"][0]["Sns"]["Message"])
context.log("Got an event from ECS: {}".format(message))
@app.s3_event_handler(bucket="myS3bucket",
events=["s3:ObjectCreated:*"], prefix="foo", suffix=".bar")
def monitor_s3(event, context):
message = json.loads(event["Records"][0]["Sns"]["Message"])
context.log("Got an event from S3: {}".format(message))
•
•
•
•
•
•
•
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜
RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜

RESTful API を Chalice で紐解く 〜 Python Serverless Microframework for AWS 〜

  • 6.
  • 7.
  • 11.
  • 12.
  • 14.
  • 15.
    Auto Scaling group Securitygroup Elastic Load Balancing Instance REST API Amazon DynamoDB Amazon CloudWatch Amazon S3
  • 16.
  • 18.
    Lambda.GetFunction(params: {'body': '','url': u'https://lambda.us-west-2.amazonaws.com/2015-03-31 IAM.GetRole(params: {'body': {'Action': u'GetRole', 'RoleName': u'helloworld7', 'Version': u'2010- IAM.CreateRole(params: {'body': {'Action': u'CreateRole', 'RoleName': u'helloworld7', 'Version': u IAM.PutRolePolicy(params: {'body': {'Action': u'PutRolePolicy', 'RoleName': u'helloworld7', 'Polic Lambda.CreateFunction(params: (... omitted from logs due to size ...) APIGateway.GetRestApis(params: {'body': '', 'url': u'https://apigateway.us-west-2.amazonaws.com/re APIGateway.CreateRestApi(params: {'body': '{"name": "helloworld7"}', 'url': u'https://apigateway.u APIGateway.GetResources(params: {'body': '', 'url': u'https://apigateway.us-west-2.amazonaws.com/r APIGateway.PutMethod(params: {'body': '{"authorizationType": "NONE"}', 'url': u'https://apigateway APIGateway.PutIntegration(params: {'body': '{"httpMethod": "POST", "requestTemplates": {"applicati APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}', APIGateway.PutIntegrationResponse(params: {'body': '{"responseTemplates": {"application/json": ""} APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}', APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "ChaliceViewError.*", "re APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}', APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "BadRequestError.*", "res APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}', APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "NotFoundError.*", "respo APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}', APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "UnauthorizedError.*", "r APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}', APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "ForbiddenError.*", "resp APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}', APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "ConflictError.*", "respo APIGateway.PutMethodResponse(params: {'body': '{"responseModels": {"application/json": "Empty"}}', APIGateway.PutIntegrationResponse(params: {'body': '{"selectionPattern": "TooManyRequestsError.*", Lambda.GetPolicy(params: {'body': '', 'url': u'https://lambda.us-west-2.amazonaws.com/2015-03-31/f Lambda.AddPermission(params: {'body': '{"Action": "lambda:InvokeFunction", "StatementId": "1e96468 APIGatewayCreateDeployment(params: {'body': '{"stageName": "dev"}', 'url': u'https://apigateway.us API # AWS SAM
  • 20.
  • 21.
  • 22.
    $ pip installchalice $ chalice new-project helloworld && cd helloworld $ cat app.py from chalice import Chalice app = Chalice(app_name="helloworld") @app.route("/") def index(): return {"hello": "world"}
  • 23.
    $ chalice deploy ... https://endpoint/api $curl https://endpoint/api {"hello": "world"}
  • 24.
    $ git diff fromchalice import Chalice +import boto3 app = Chalice(app_name='chalice-sample') +S3 = boto3.client('s3', region_name='us-east-1') @app.route('/') def index(): + S3.get_object(Bucket = BUCKET, Key = key) return {'hello': 'world'}
  • 25.
    $ chalice deploy Creatingrole: chalice-sample-dev The following execution policy will be used: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject" ], "Resource": ["*"], "Sid": "40e10c45a..." }, ... } Would you like to continue? [Y/n]:
  • 26.
    $ chalice local Servingon localhost:8000 $ npm install -g nodemon $ nodemon --exec "chalice local" --watch *.py [nodemon] 1.12.5 [nodemon] to restart at any time, enter `rs` [nodemon] watching: app.py [nodemon] starting `chalice local` Serving on localhost:8000 see@ https://qiita.com/TakenoriHirao/items/69f2af5aaf64db77124b
  • 27.
  • 29.
    # Basic Definition @app.route('/') defindex(): return {"GET": "/"}
  • 30.
    # Specify Methods @app.route('/',methods=['PUT']) def index_put(): return {"PUT": "/"}
  • 31.
    @app.route('/res', methods=['GET', 'POST']) defmy_resource(): request = app.current_request if request.method == 'GET': return {"GET": "/res"} elif request.method == 'POST': return {"POST": "/res"}
  • 32.
    # Path Params @app.route('/myresources/{object_name}') defmy_resource_key(object_name): try: response = S3.get_object( Bucket = BUCKET, Key = object_name) return response['Body'].read() except ClientError as e: raise e
  • 33.
    @app.route('/s3/{bucket_name}/objects') def objects_of(bucket_name): try: response = S3.list_objects_v2(Bucket=bucket_name, Prefix= S3_PREFIX) objects = list(map(lambda x: x["Key"], response["Contents"])) return {"objects": objects} except ClientError as e: raise ...
  • 34.
    @app.route('/') def index(): return Response( body= 'hello world!', status_code = 200, headers = {'Content-Type': 'text/plain'} )
  • 36.
  • 38.
  • 39.
    class Chalice(object): def route(self,path, **kwargs): # def _register_view(view_func): self._add_route(path, view_func, **kwargs) return view_func return _register_view def _add_route(self, path, view_func, **kwargs): methods = kwargs.pop('methods', ['GET']) ... for method in methods: ... entry = RouteEntry(view_func, name, path, method, api_key_required, content_types, cors, authorizer) self.routes[path][method] = entry
  • 40.
    class Chalice(object): def __call__(self,event, context): # resource_path = event.get('requestContext', {}).get('resourcePath') http_method = event['requestContext']['httpMethod'] route_entry = self.routes[resource_path][http_method] view_function = route_entry.view_function function_args = {name: event['pathParameters'][name] for name in route_entry.view_args} ... response = self._get_view_function_response(view_function, function_args) response_headers = CaseInsensitiveMapping(response.headers) ... response = response.to_dict(self.api.binary_types) return response
  • 41.
    class Chalice(object): def _get_view_function_response(self,view_function, function_args): try: response = view_function(**function_args) if not isinstance(response, Response): response = Response(body=response) self._validate_response(response) except ChaliceViewError as e: response = Response(...) except Exception as e: headers = {} if self.debug: ... else: response = Response(..., status_code=500) return response
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
    @app.sns_topic_subscriber("bartender") def tend(event, context): message= json.loads(event["Records"][0]["Sns"]["Message"]) context.log(dict(beer="Quadrupel", quantity=message["beer"])) @app.cloudwatch_event_handler(source=["aws.ecs"]) def monitor_ecs_events(event, context): message = json.loads(event["Records"][0]["Sns"]["Message"]) context.log("Got an event from ECS: {}".format(message)) @app.s3_event_handler(bucket="myS3bucket", events=["s3:ObjectCreated:*"], prefix="foo", suffix=".bar") def monitor_s3(event, context): message = json.loads(event["Records"][0]["Sns"]["Message"]) context.log("Got an event from S3: {}".format(message))
  • 49.