Version Amazon API Gateway et AWS Lambda Python

Sans serveur en combinant Amazon API Gateway et AWS Lambda J'écrirai sur la réalisation de l'architecture. En Python.

API Gateway

Qu'est-ce que API Gateway?

API Gateway est un service qui agit comme un wrapper pour fournir l'API Web Restful. Il semble qu'il soit également combiné avec CloudFront, ou mis en œuvre en tant que wrapper pour celui-ci. CloudFront a l'image du CDN, mais comme AWS WAF est également combiné avec CloudFront, il se positionne comme un service réseau de couche 7, non limité au CDN. Cela peut avoir été. Avec CDN seul, il y a de nombreuses compétitions et, dans un sens, il semble qu'il deviendra une marchandise.

Composants API Gateway

échantillon

API Gateway Comme c'est un gros problème, j'ai créé une API à partir de Python. Modifiez la région, la fonction et le rôle au début selon vos besoins.

createapi.py


# -*- coding: utf-8 -*-

import boto3
client = boto3.client('apigateway')
region = 'ap-northeast-1'
function = 'arn:aws:lambda:ap-northeast-1:AWS_ACCOUNT_ID:function:YOUR_LAMBDA_FUNCTION'
role = 'arn:aws:iam::AWS_ACCOUNT_ID:role/YOUR_IAM_ROLE_FOR_INVOCATION'


def create_api():
    rest_api = client.create_rest_api(
        name='sample01',
        description='sample api',
    )
    return rest_api['id']


def create_resource(rest_api_id):
    for resource in client.get_resources(
        restApiId=rest_api_id
    )['items']:
        if resource['path'] == '/':
            path_root_id = resource['id']
    new_resource = client.create_resource(
        restApiId=rest_api_id,
        parentId=path_root_id,
        pathPart='{hoge}',
    )
    return new_resource['id']


def setup_method(rest_api_id, resource_id):
    client.put_method(
        restApiId=rest_api_id,
        resourceId=resource_id,
        httpMethod='GET',
        authorizationType='NONE',
    )
    uri = 'arn:aws:apigateway:' + region + ':lambda:path/2015-03-31/functions/' + function + '/invocations'
    client.put_integration(
        restApiId=rest_api_id,
        resourceId=resource_id,
        httpMethod='GET',
        type='AWS',
        integrationHttpMethod='POST',
        uri=uri,
        credentials=role,
        requestTemplates={
            'application/json': get_request_template()
        },
    )
    client.put_integration_response(
        restApiId=rest_api_id,
        resourceId=resource_id,
        httpMethod='GET',
        statusCode='200',
        responseTemplates={
            'application/json': '',
        },
    )
    client.put_method_response(
        restApiId=rest_api_id,
        resourceId=resource_id,
        httpMethod='GET',
        statusCode='200',
        responseModels={
            'application/json': 'Empty',
        },
    )
    client.put_integration_response(
        restApiId=rest_api_id,
        resourceId=resource_id,
        httpMethod='GET',
        statusCode='400',
        selectionPattern='^\[400:.*',
        responseTemplates={
            'application/json': get_response_template(),
        },
    )
    client.put_method_response(
        restApiId=rest_api_id,
        resourceId=resource_id,
        httpMethod='GET',
        statusCode='400',
        responseModels={
            'application/json': 'Error',
        },
    )


def get_request_template():
    """
    ref. http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
    """
    return """
{
  "pathParams": {
#foreach ($key in $input.params().path.keySet())
    "$key": "$util.escapeJavaScript($input.params().path.get($key))"#if ($foreach.hasNext),#end
#end
  }
}
"""


def get_response_template():
    """
    ref. http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
    """
    return """
#set($data = $input.path('$'))
{
  "message" : "${data.errorMessage}"
}
"""


def deploy(rest_api_id):
    client.create_deployment(
        restApiId=rest_api_id,
        stageName='snapshot',
        stageDescription='snapshot stage',
    )
    client.update_stage(
        restApiId=rest_api_id,
        stageName='snapshot',
        patchOperations=[
            {
                'op': 'replace',
                'path': '/*/*/logging/loglevel',
                'value': 'INFO',
            },
        ],
    )

if __name__ == '__main__':
    rest_api_id = create_api()
    resource_id = create_resource(rest_api_id)
    setup_method(rest_api_id, resource_id)
    deploy(rest_api_id)
    api_url = 'https://' + rest_api_id + '.execute-api.' + region + '.amazonaws.com/snapshot/'
    print 'OK : {0}'.format(api_url + 'hoge')
    print 'NG : {0}'.format(api_url + 'fuga')

Lambda Comment déployer Lambda est hors de question, donc juste le code.

lambdemo.py


# -*- coding: utf-8 -*-


def lambda_handler(event, context):
    hoge = event['pathParams']['hoge']
    if hoge == 'hoge':
        return {'message': 'hogehoge'}
    else:
        raise NotHogeError(hoge)


class NotHogeError(Exception):
    def __init__(self, hoge):
        self.hoge = hoge

    def __str__(self):
        return '[400:BadRequest] {0} is not hoge'.format(self.hoge)

Contrôle de fonctionnement

Si vous exécutez createapi.py ci-dessus, vous verrez deux URL comme celle-ci (s'il n'y a pas de bogues).

createapi.Résultat d'exécution de py


OK : https://xxxxx.execute-api.somewhere.amazonaws.com/snapshot/hoge
NG : https://xxxxx.execute-api.somewhere.amazonaws.com/snapshot/fuga

Lorsque vous accédez à la personne OK

OK


$ curl https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/snapshot/hoge
{"message": "hogehoge"}

Si vous êtes une personne NG, vous vous fâcherez.

NG


$ curl https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/snapshot/fuga  -w '%{http_code}\n'
{
  "message" : "[400:BadRequest] fuga is not hoge"
}
400

La description

Integration

URI Lambda

Vous devez spécifier l'URI du backend Lambda. C'est compliqué, mais il semble avoir une spécification fixe, qui est décidée par la région et l'ARN de la fonction Lambda. Si vous omettez les dernières "invocations", il renverra "500 erreur de serveur interne". Un point d'ajustement sobre.

Comment créer un URI


uri = 'arn:aws:apigateway:' + region + ':lambda:path/2015-03-31/functions/' + function + '/invocations'

Demande de conversion de format

Faites une demande (json) pour Lambda à l'aide du modèle Velocity. Voir Référence (http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html) pour les variables disponibles. Bien qu'il soit codé en dur dans l'exemple, je pense qu'il est préférable de sortir en tant que fichier modèle en fonctionnement réel.

request_template.vm


{
  "pathParams": {
#foreach ($key in $input.params().path.keySet())
    "$key": "$util.escapeJavaScript($input.params().path.get($key))"#if ($foreach.hasNext),#end
#end
  }
}

Comment faire Lambda

demande

Dans le cas de Python, les données converties au format ci-dessus sont incluses dans event en tant que dict.

Obtenir la demande


hoge = event['pathParams']['hoge']

réponse

Utilisez return ou rise pour renvoyer une réponse de Lambda à API Gateway.

Réussite


return {'message': 'hogehoge'}

Terminaison anormale


raise NotHogeError(hoge)

Comme expliqué ci-après, si vous le retournez avec retour, il sera retourné au client avec le code de statut 200. (Pour être exact, c'est le code de statut par défaut ...)

Integration Response Convertit la réponse de Lambda en réponse client.

Si vous ne spécifiez pas de modèle (définissez-le sur «»), la réponse de lambda sera renvoyée telle quelle. Dans ce cas, définissez le modèle de réponse sur Empty dans Method Response.

Il est facile de renvoyer le code d'état avec 200, mais il est également nécessaire de le renvoyer avec une erreur client (série 400), une erreur serveur (série 500) ou une redirection (série 300). Pour cela, il est nécessaire d'associer à une expression régulière le type de réponse et le code d'état à utiliser.

Où recherchent les expressions régulières

Réécrivons maintenant l'exemple Lambda.

Quand j'essaye de revenir


else:
        #raise NotHogeError(hoge)
        return '[400:BadRequest] {0} is not hoge'.format(hoge)

Reviendra à 200


$ curl https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/snapshot/fuga -w '%{http_code}\n'
"[400:BadRequest] fuga is not hoge"200

Le code d'état est devenu 200. .. .. En fait, vous ne regardez pas la valeur de retour du gestionnaire Lambda.

C'est aussi


return {'message' : '[400:BadRequest] {0} is not hoge'.format(hoge)}

Ce n'est pas bien


return {'errorMessage' : '[400:BadRequest] {0} is not hoge'.format(hoge)}

C'est ce à quoi je suis arrivé de différentes manières.

exception.py


class NotHogeError(Exception):
    def __init__(self, hoge):
        self.hoge = hoge

    def __str__(self):
        """ici!"""
        return '[400:BadRequest] {0} is not hoge'.format(self.hoge)

Ainsi, il semble que vous devriez lever une exception lorsque vous souhaitez modifier le code d'état.

Conversion de format de réponse

Dans le cas d'un système normal, la réponse de Lambda doit être renvoyée par pass-through, mais si elle est renvoyée en XML ou HTML au lieu de json, elle doit être convertie. De plus, dans le cas d'un système anormal, s'il s'agit d'un pass-through, StackTrace sera également renvoyé et ce sera pitoyable, donc seul le message d'erreur doit être renvoyé.

À l'origine, un modèle de réponse appelé Erreur est défini, créez donc un modèle en fonction de celui-ci.

response_template.vm


#set($data = $input.path('$'))
{
  "message" : "${data.errorMessage}"
}

Résumé

Je pensais qu'API Gateway n'était pas si simple, mais j'avais envie d'y penser si je voulais concevoir correctement une API en premier lieu.

Recommended Posts

Version Amazon API Gateway et AWS Lambda Python
[Python] J'ai écrit une API REST en utilisant AWS API Gateway et Lambda.
AWS CDK-Lambda + API Gateway (Python)
[AWS] Créer une API avec API Gateway + Lambda
Essayez d'implémenter un robot Cisco Spark avec AWS Lambda + Amazon API Gateway (Python)
Créez rapidement une API avec Python, lambda et API Gateway à l'aide d'AWS SAM
[AWS SAM] Créer une API avec DynamoDB + Lambda + API Gateway
[AWS] Essayez de tracer API Gateway + Lambda avec X-Ray
Créez des tweets ordinaires comme une flotte avec AWS Lambda et Python
[Python] Scraping dans AWS Lambda
Surveillance du site et notification d'alerte avec AWS Lambda + Python + Slack
Ecrire une fonction AWS Lambda en Python
Exécutez Python selon la planification sur AWS Lambda
Coopération entre le module python et l'API
mappe de traitement de chaîne python et lambda
Notifier HipChat avec AWS Lambda (Python)
[AWS SAM] Présentation de la version Python
Envoyer les images prises avec ESP32-WROOM-32 vers AWS (API Gateway → Lambda → S3)
J'ai comparé Node.js et Python lors de la création d'une miniature à l'aide d'AWS Lambda
J'ai essayé ChatOps avec Slack x API Gateway x Lambda (Python) x RDS
ImportError lors de la tentative d'utilisation du package gcloud avec la version AWS Lambda Python
[AWS] Utilisation de fichiers ini avec Lambda [Python]
Python appelant l'API Google Cloud Vision depuis LINE BOT via AWS Lambda
[AWS] Créez un environnement Python Lambda avec CodeStar et faites Hello World
Obtenez des données alimentaires avec l'API Amazon (Python)
Surveillance simple du serveur avec AWS Lambda (Python) et notification des résultats avec Slack
Présentation du mécanisme Twilio # 3-1 - Procédure pas à pas de mise en œuvre de la passerelle API API + Lambda (partie 1)
[AWS] Associez Lambda et S3 à boto3
[Python] Exécutez Headless Chrome sur AWS Lambda
Connectez-vous à s3 avec AWS Lambda Python
API REST facile avec API Gateway / Lambda / DynamoDB
Touchez AWS avec Serverless Framework et Python
Une histoire de compilation croisée d'un package Python pour AWS Lambda et de son déploiement sans serveur
Exécutez régulièrement des programmes Python sur AWS Lambda
[Version 2020] Comment installer Python3 sur EC2 d'AWS
Différences entre queryStringParameters et multiValueQueryStringParameters dans AWS Lambda
Afficher les images sur S3 avec API Gateway + Lambda
Application sans serveur avec AWS SAM! (APIGATEWAY + Lambda (Python))
[AWS / Lambda] Comment charger une bibliothèque externe Python
Résumé de l'étude de Python pour utiliser AWS Lambda
Obtenez l'objet et le corps de Gmail avec Python et l'API Gmail
Transmettre Cognito Id à Lambda via API Gateway
Essayez d'utiliser l'API ChatWork et l'API Qiita en Python
Installez la version ZIP Python et pip sur Windows 10
Remarque pour le traitement des données POST en se connectant à Lambda via la passerelle API d'AWS (API HTTP)
Essayez d'exécuter Schedule pour démarrer et arrêter des instances dans AWS Lambda (Python)
Version 64 bits de PYTHON2.7
"Régression linéaire" et "Version probabiliste de la régression linéaire" en Python "Régression linéaire de Bayes"
Développer, exécuter et déployer AWS Lambda à distance à l'aide de lambda-uploader
J'ai essayé pipenv et asdf pour le contrôle de version Python
Terraform configuré pour lancer AWS Lambda à partir d'Amazon SQS
Créer une fonction Lambda de version Python (+ couche Lambda) avec Serverless Framework
LiNGAM (version ICA) à comprendre avec des formules mathématiques et Python
Vérifiez types_map lors de l'utilisation de mimetypes avec AWS Lambda (Python)
[AWS] Faites-vous des amis avec l'entrée JSON de Lambda (version Python)