Amazon API Gateway und AWS Lambda Python-Version

Serverlos durch Kombination von Amazon API Gateway und AWS Lambda Ich werde über die Realisierung der Architektur schreiben. In Python.

API Gateway

Was ist API Gateway?

API Gateway ist ein Dienst, der als Wrapper für die Bereitstellung der Restful Web API fungiert. Es scheint, dass es auch mit CloudFront kombiniert oder als Wrapper dafür implementiert ist. CloudFront hat das Image von CDN, aber da AWS WAF auch mit CloudFront kombiniert wird, wird es als Layer 7-Netzwerkdienst positioniert, nicht nur für CDN. Es kann gewesen sein. Allein mit CDN gibt es viele Wettbewerbe, und in gewissem Sinne scheint es, dass es eine Ware werden wird.

API-Gateway-Komponenten

Stichprobe

API Gateway Da es eine große Sache ist, habe ich eine API aus Python erstellt. Ändern Sie die Region, Funktion und Rolle am Anfang entsprechend.

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 Wie Lambda bereitgestellt wird, kommt nicht in Frage, also nur der 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)

Funktionsprüfung

Wenn Sie die Datei createapi.py oben ausführen, werden zwei URLs wie diese angezeigt (sofern keine Fehler vorliegen).

createapi.Ausführungsergebnis von py


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

Wenn Sie auf die OK-Person zugreifen

OK


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

Wenn Sie eine NG-Person sind, werden Sie wütend.

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

Erläuterung

Integration

Lambda URI

Sie müssen den URI des Backend-Lambda angeben. Es ist kompliziert, aber es scheint eine feste Spezifikation zu haben, die von der Region und der Lambda-Funktion ARN festgelegt wird. Wenn Sie die letzten "Aufrufe" weglassen, wird "500 interner Serverfehler" zurückgegeben. Ein nüchterner Anpassungspunkt.

So erstellen Sie eine URI


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

Formatkonvertierung anfordern

Stellen Sie mithilfe der Velocity-Vorlage eine Anfrage (json) für Lambda. Die verfügbaren Variablen finden Sie unter Referenz (http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html). Obwohl es im Beispiel fest codiert ist, denke ich, dass es besser ist, im tatsächlichen Betrieb als Vorlagendatei auszugehen.

request_template.vm


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

Wie man Lambda macht

Anfrage

Im Fall von Python werden die im obigen Format konvertierten Daten als Diktat in event aufgenommen.

Anfrage erhalten


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

Antwort

Verwenden Sie return oder raise, um eine Antwort von Lambda an API Gateway zurückzugeben.

Erfolgreiche Fertigstellung


return {'message': 'hogehoge'}

Abnormale Beendigung


raise NotHogeError(hoge)

Wie im Folgenden erläutert, wird es mit dem Statuscode 200 an den Client zurückgegeben, wenn Sie es mit return zurückgeben. (Um genau zu sein, ist es der Standardstatuscode ...)

Integration Response Konvertiert die Antwort von Lambda in eine Client-Antwort.

Wenn Sie keine Vorlage angeben (auf '' setzen), wird die Antwort von Lambda unverändert zurückgegeben. Setzen Sie in diesem Fall das Antwortmodell in Method Response auf Empty.

Es ist einfach, den Statuscode mit 200 zurückzugeben, aber es ist auch erforderlich, ihn mit einem Clientfehler (Serie 400), einem Serverfehler (Serie 500) oder einer Umleitung (Serie 300) zurückzugeben. Zu diesem Zweck muss einem regulären Ausdruck zugeordnet werden, welche Art von Antwort und welcher Statuscode verwendet werden soll.

Wo suchen die regulären Ausdrücke?

Schreiben wir nun das Beispiel Lambda neu.

Wenn ich versuche zurückzukehren


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

Kommt um 200 zurück


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

Der Statuscode ist 200 geworden. .. .. Tatsächlich sehen Sie nicht den Rückgabewert des Lambda-Handlers.

Das ist auch


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

Das ist nicht gut


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

Darauf bin ich auf verschiedene Weise gekommen.

exception.py


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

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

Es scheint also, dass Sie eine Ausnahme auslösen sollten, wenn Sie den Statuscode ändern möchten.

Konvertierung des Antwortformats

Bei einem normalen System sollte die Antwort von Lambda per Pass-Through zurückgegeben werden. Wenn sie jedoch von XML oder HTML anstelle von json zurückgegeben wird, muss sie konvertiert werden. Im Falle eines abnormalen Systems wird StackTrace ebenfalls zurückgegeben und ist erbärmlich, sodass nur die Fehlermeldung zurückgegeben werden sollte.

Ursprünglich ist ein Antwortmodell mit dem Namen "Fehler" definiert. Erstellen Sie daher eine entsprechende Vorlage.

response_template.vm


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

Zusammenfassung

Ich dachte, dass API Gateway nicht so einfach ist, aber ich wollte darüber nachdenken, ob ich eine API überhaupt richtig entwerfen wollte.

Recommended Posts

Amazon API Gateway und AWS Lambda Python-Version
[Python] Ich habe eine REST-API mit AWS API Gateway und Lambda geschrieben.
AWS CDK-Lambda + API-Gateway (Python)
[AWS] API mit API Gateway + Lambda erstellen
Versuchen Sie, einen Cisco Spark Bot mit AWS Lambda + Amazon API Gateway (Python) zu implementieren.
Erstellen Sie mit AWS SAM schnell eine API mit Python, Lambda und API Gateway
[AWS SAM] Erstellen Sie eine API mit DynamoDB + Lambda + API Gateway
[AWS] Versuchen Sie, API Gateway + Lambda mit X-Ray zu verfolgen
Machen Sie mit AWS Lambda und Python gewöhnliche Tweets flottenartig
[Python] Scraping in AWS Lambda
Site-Überwachung und Alarmbenachrichtigung mit AWS Lambda + Python + Slack
Schreiben Sie die AWS Lambda-Funktion in Python
Führen Sie Python planmäßig auf AWS Lambda aus
Zusammenarbeit zwischen Python-Modul und API
Python String Processing Map und Lambda
Benachrichtigen Sie HipChat mit AWS Lambda (Python)
[AWS SAM] Einführung in die Python-Version
Senden Sie mit ESP32-WROOM-32 aufgenommene Bilder an AWS (API Gateway → Lambda → S3).
Ich habe Node.js und Python beim Erstellen eines Miniaturbilds mit AWS Lambda verglichen
Ich habe ChatOps mit Slack x API Gateway x Lambda (Python) x RDS ausprobiert
ImportError beim Versuch, das gcloud-Paket mit der AWS Lambda Python-Version zu verwenden
[AWS] Verwenden von INI-Dateien mit Lambda [Python]
Python ruft die Google Cloud Vision API von LINE BOT über AWS Lambda auf
[AWS] Erstellen Sie mit CodeStar eine Python Lambda-Umgebung und führen Sie Hello World aus
Holen Sie sich Lebensmitteldaten mit Amazon API (Python)
Einfache Serverüberwachung mit AWS Lambda (Python) und Ergebnisbenachrichtigung mit Slack
Verständnis anhand des Mechanismus Twilio # 3-1 --AWS API Gateway + Lambda-Implementierung Walkthrough (Teil 1)
[AWS] Verknüpfen Sie Lambda und S3 mit boto3
[Python] Führen Sie Headless Chrome unter AWS Lambda aus
Stellen Sie mit AWS Lambda Python eine Verbindung zu s3 her
Einfache REST-API mit API Gateway / Lambda / DynamoDB
Berühren Sie AWS mit Serverless Framework und Python
Eine Geschichte über das Cross-Kompilieren eines Python-Pakets für AWS Lambda und das Bereitstellen ohne Server
Führen Sie regelmäßig Python-Programme auf AWS Lambda aus
[Version 2020] So installieren Sie Python3 auf EC2 von AWS
Unterschiede zwischen queryStringParameters und multiValueQueryStringParameters in AWS Lambda
Zeigen Sie Bilder in S3 mit API Gateway + Lambda an
Serverlose Anwendung mit AWS SAM! (APIGATEWAY + Lambda (Python))
[AWS / Lambda] Laden einer externen Python-Bibliothek
Zusammenfassung des Studiums von Python zur Verwendung von AWS Lambda
Holen Sie sich den Betreff und den Text von Google Mail mit der Python- und Google Mail-API
Übergeben Sie die Cognito-ID über das API-Gateway an Lambda
Versuchen Sie, die ChatWork-API und die Qiita-API in Python zu verwenden
Installieren Sie die ZIP-Version Python und Pip unter Windows 10
Hinweis zur Verarbeitung von POST-Daten durch Herstellen einer Verbindung zu Lambda über das AWS API Gateway (HTTP API)
Versuchen Sie, Schedule auszuführen, um Instanzen in AWS Lambda (Python) zu starten und zu stoppen.
PYTHON2.7 64-Bit-Version
"Lineare Regression" und "Probabilistische Version der linearen Regression" in Python "Bayes lineare Regression"
Entwickeln, Ausführen und Bereitstellen von AWS Lambda remote mit dem Lambda-Uploader
Ich habe pipenv und asdf für die Python-Versionskontrolle ausprobiert
Terraform konfiguriert, um AWS Lambda von Amazon SQS aus zu starten
Erstellen Sie eine Python-Version der Lambda-Funktion (+ Lambda-Schicht) mit Serverless Framework
LiNGAM (ICA-Version) mit mathematischen Formeln und Python zu verstehen
Überprüfen Sie types_map, wenn Sie Mimetypen mit AWS Lambda (Python) verwenden.
[AWS] Freunde dich mit Lambdas JSON-Eingabe an (Python-Version)