AWS Lambda gebruiken met API Gateway

1. Overzicht

AWS Lambda is een serverloze computerservice die wordt aangeboden door Amazon Web Services.

In twee eerdere artikelen hebben we besproken hoe je een AWS Lambda-functie kunt maken met Java, en hoe je toegang kunt krijgen tot DynamoDB vanuit een Lambda-functie.

In deze tutorial bespreken we hoe u een Lambda-functie publiceert als een REST-eindpunt, met behulp van AWS Gateway.

We zullen de volgende onderwerpen uitgebreid bekijken:

  • Basisconcepten en voorwaarden van API Gateway
  • Integratie van Lambda-functies met API Gateway met behulp van Lambda Proxy-integratie
  • Creëren van een API, de structuur ervan en hoe de API-bronnen kunnen worden toegewezen aan Lambda-functies
  • Implementatie en test van de API

2. Basisprincipes en voorwaarden

API Gateway is een volledig beheerde service waarmee ontwikkelaars op elke schaal API's kunnen maken, publiceren, onderhouden, bewaken en beveiligen.

We kunnen een consistente en schaalbare op HTTP gebaseerde programmeerinterface implementeren (ook wel RESTful-services genoemd) om toegang te krijgen tot backend-services zoals Lambda-functies, andere AWS-services (bijv.EC2, S3, DynamoDB) en alle HTTP-eindpunten.

Functies omvatten, maar zijn niet beperkt tot:

  • Verkeersmanagement
  • Autorisatie en toegangscontrole
  • Toezicht houden
  • API-versiebeheer
  • Verzoeken beperken om aanvallen te voorkomen

Net als AWS Lambda wordt API Gateway automatisch uitgeschaald en in rekening gebracht per API-aanroep.

Gedetailleerde informatie is te vinden in de officiële documentatie.

2.1. Voorwaarden

API-gateway is een AWS-service die het maken, implementeren en beheren van een RESTful-applicatieprogrammeringsinterface ondersteunt om back-end HTTP-eindpunten, AWS Lambda-functies en andere AWS-services bloot te leggen.

Een API Gateway API is een verzameling bronnen en methoden die kunnen worden geïntegreerd met Lambda-functies, andere AWS-services of HTTP-eindpunten in de backend. De API bestaat uit bronnen die de API-structuur vormen. Elke API-resource kan een of meer API-methoden weergeven die unieke HTTP-werkwoorden moeten hebben.

Om een ​​API te publiceren, moeten we een API-implementatie en associeer het met een zogenaamd stadium. Een fase is als een momentopname van de API. Als we een API opnieuw implementeren, kunnen we een bestaande fase bijwerken of een nieuwe maken. Daardoor zijn verschillende versies van een API tegelijkertijd mogelijk, bijvoorbeeld een dev podium, een test stage, en zelfs meerdere productieversies, zoals v1, v2, enz.

Lambda Proxy-integratie is een vereenvoudigde configuratie voor de integratie tussen Lambda-functies en API Gateway.

De API Gateway stuurt het volledige verzoek als invoer naar een back-end Lambda-functie. Qua respons transformeert API Gateway de uitvoer van de Lambda-functie terug naar een front-end HTTP-respons.

3. Afhankelijkheden

We hebben dezelfde afhankelijkheden nodig als in het artikel AWS Lambda DynamoDB gebruiken met Java.

Bovendien hebben we ook de JSON Simple-bibliotheek nodig:

 com.googlecode.json-simple json-simple 1.1.1 

4. Ontwikkeling en implementatie van de Lambda-functies

In deze sectie ontwikkelen en bouwen we onze Lambda-functies in Java, implementeren we deze met AWS Console en voeren we een snelle test uit.

Omdat we de basismogelijkheden van het integreren van API Gateway met Lambda willen demonstreren, zullen we twee functies creëren:

  • Functie 1: ontvangt een payload van de API, met behulp van een PUT-methode
  • Functie 2: laat zien hoe u een HTTP-padparameter of HTTP-queryparameter gebruikt die afkomstig is van de API

Implementatie-wijs, we zullen er een maken RequestHandler class, die twee methoden heeft - een voor elke functie.

4.1. Model

Laten we, voordat we de daadwerkelijke aanvraagbehandelaar implementeren, even kijken naar ons datamodel:

openbare klasse Persoon {privé int id; private String naam; publieke persoon (String json) {Gson gson = new Gson (); Persoonverzoek = gson.fromJson (json, Person.class); this.id = request.getId (); this.name = request.getName (); } openbare String toString () {Gson gson = nieuwe GsonBuilder (). setPrettyPrinting (). create (); retourneer gson.toJson (dit); } // getters en setters}

Ons model bestaat uit één eenvoudig Persoon class, die twee eigenschappen heeft. Het enige opvallende deel is de Persoon (tekenreeks) constructor, die een JSON-string accepteert.

4.2. Implementatie van de RequestHandler-klasse

Net als in het AWS Lambda With Java-artikel, maken we een implementatie van het RequestStreamHandler koppel:

openbare klasse APIDemoHandler implementeert RequestStreamHandler {privé statische laatste String DYNAMODB_TABLE_NAME = System.getenv ("TABLE_NAME"); @Override public void handleRequest (InputStream inputStream, OutputStream outputStream, Context context) gooit IOException {// implementatie} public void handleGetByParam (InputStream inputStream, OutputStream outputStream, Context context) gooit IOException {// implementatie}}

Zoals we kunnen zien, is de RequestStreamHander interface definieert slechts één methode, handeRequest (). Hoe dan ook, we kunnen meer functies in dezelfde klasse definiëren, zoals we hier hebben gedaan. Een andere optie zou zijn om één implementatie van RequestStreamHander voor elke functie.

In ons specifieke geval hebben we vanwege de eenvoud het eerste gekozen. De keuze moet echter van geval tot geval worden gemaakt, rekening houdend met factoren als prestaties en onderhoudbaarheid van de code.

We lezen ook de naam van onze DynamoDB-tabel uit de TAFEL NAAM omgevingsvariabele. We zullen die variabele later tijdens de implementatie definiëren.

4.3. Implementatie van functie 1

In onze eerste functie willen we demonstreren hoe u een payload kunt krijgen (zoals van een PUT- of POST-verzoek) van de API Gateway:

public void handleRequest (InputStream inputStream, OutputStream outputStream, Context context) gooit IOException {JSONParser parser = nieuwe JSONParser (); BufferedReader-lezer = nieuwe BufferedReader (nieuwe InputStreamReader (inputStream)); JSONObject responseJson = nieuw JSONObject (); AmazonDynamoDB-client = AmazonDynamoDBClientBuilder.defaultClient (); DynamoDB dynamoDb = nieuwe DynamoDB (client); probeer {JSONObject event = (JSONObject) parser.parse (reader); if (event.get ("body")! = null) {Persoon person = nieuwe persoon ((String) event.get ("body")); dynamoDb.getTable (DYNAMODB_TABLE_NAME) .putItem (new PutItemSpec (). withItem (new Item (). withNumber ("id", person.getId ()) .withString ("name", person.getName ()))); } JSONObject responseBody = nieuw JSONObject (); responseBody.put ("bericht", "Nieuw item aangemaakt"); JSONObject headerJson = nieuw JSONObject (); headerJson.put ("x-custom-header", "mijn aangepaste headerwaarde"); responseJson.put ("statusCode", 200); responseJson.put ("headers", headerJson); responseJson.put ("body", responseBody.toString ()); } catch (ParseException pex) {responseJson.put ("statusCode", 400); responseJson.put ("uitzondering", pex); } OutputStreamWriter writer = nieuwe OutputStreamWriter (outputStream, "UTF-8"); writer.write (responseJson.toString ()); schrijver.close (); }

Zoals eerder besproken, zullen we de API later configureren om Lambda-proxy-integratie te gebruiken. We verwachten dat de API Gateway het volledige verzoek doorgeeft aan de Lambda-functie in het InputStream parameter.

Het enige wat we hoeven te doen is de relevante attributen kiezen uit de ingesloten JSON-structuur.

Zoals we kunnen zien, bestaat de methode in feite uit drie stappen:

  1. Ophalen van het lichaam object uit onze invoerstroom en het maken van een Persoon bezwaar daar tegen
  2. Dat bewaren Persoon object in een DynamoDB-tabel
  3. Een JSON-object bouwen dat verschillende attributen kan bevatten, zoals een lichaam voor het antwoord, aangepaste headers, evenals een HTTP-statuscode

Een punt dat het vermelden waard is: API Gateway verwacht het lichaam een ... zijn Draad (voor zowel verzoek als antwoord).

Zoals we verwachten een Draad net zo lichaam van de API Gateway casten we de lichaam naar Draad en initialiseer onze Persoon voorwerp:

Persoon person = nieuwe persoon ((String) event.get ("body"));

API Gateway verwacht ook de reactie lichaam een ... zijn Draad:

responseJson.put ("body", responseBody.toString ());

Dit onderwerp wordt niet expliciet genoemd in de officiële documentatie. Als we echter goed kijken, kunnen we zien dat het lichaamsattribuut a is Draad in beide fragmenten voor zowel het verzoek als voor het antwoord.

Het voordeel moet duidelijk zijn: zelfs als JSON het formaat is tussen API Gateway en de Lambda-functie, kan de daadwerkelijke tekst platte tekst, JSON, XML of wat dan ook bevatten. Het is dan de verantwoordelijkheid van de Lambda-functie om het formaat correct af te handelen.

We zullen later zien hoe de aanvraag- en antwoordtekst eruitziet wanneer we onze functies testen in de AWS-console.

Hetzelfde geldt ook voor de volgende twee functies.

4.4. Implementatie van functie 2

In een tweede stap willen we demonstreren hoe u een padparameter of een querytekenreeksparameter gebruikt voor het ophalen van een Persoon item uit de database met behulp van zijn ID:

public void handleGetByParam (InputStream inputStream, OutputStream outputStream, context context) gooit IOException {JSONParser parser = nieuwe JSONParser (); BufferedReader-lezer = nieuwe BufferedReader (nieuwe InputStreamReader (inputStream)); JSONObject responseJson = nieuw JSONObject (); AmazonDynamoDB-client = AmazonDynamoDBClientBuilder.defaultClient (); DynamoDB dynamoDb = nieuwe DynamoDB (client); Item resultaat = null; probeer {JSONObject event = (JSONObject) parser.parse (reader); JSONObject responseBody = nieuw JSONObject (); if (event.get ("pathParameters")! = null) {JSONObject pps = (JSONObject) event.get ("pathParameters"); if (pps.get ("id")! = null) {int id = Integer.parseInt ((String) pps.get ("id")); result = dynamoDb.getTable (DYNAMODB_TABLE_NAME) .getItem ("id", id); }} else if (event.get ("queryStringParameters")! = null) {JSONObject qps = (JSONObject) event.get ("queryStringParameters"); if (qps.get ("id")! = null) {int id = Integer.parseInt ((String) qps.get ("id")); result = dynamoDb.getTable (DYNAMODB_TABLE_NAME) .getItem ("id", id); }} if (resultaat! = null) {Persoon persoon = nieuwe persoon (result.toJSON ()); responseBody.put ("Persoon", persoon); responseJson.put ("statusCode", 200); } else {responseBody.put ("bericht", "Geen item gevonden"); responseJson.put ("statusCode", 404); } JSONObject headerJson = nieuw JSONObject (); headerJson.put ("x-custom-header", "mijn aangepaste headerwaarde"); responseJson.put ("headers", headerJson); responseJson.put ("body", responseBody.toString ()); } catch (ParseException pex) {responseJson.put ("statusCode", 400); responseJson.put ("uitzondering", pex); } OutputStreamWriter writer = nieuwe OutputStreamWriter (outputStream, "UTF-8"); writer.write (responseJson.toString ()); schrijver.close (); }

Nogmaals, drie stappen zijn relevant:

  1. We controleren of a pathParameters of een queryStringParameters array met een ID kaart attribuut zijn aanwezig.
  2. Als waargebruiken we de bijbehorende waarde om een Persoon item met dat ID uit de database.
  3. We voegen een JSON-weergave van het ontvangen item toe aan het antwoord.

De officiële documentatie biedt een meer gedetailleerde uitleg van het invoerformaat en het uitvoerformaat voor Proxy-integratie.

4.5. Bouwcode

Nogmaals, we kunnen onze code eenvoudig bouwen met Maven:

mvn schoon pakket schaduw: schaduw

Het JAR-bestand wordt gemaakt onder de doelwit map.

4.6. De DynamoDB-tabel maken

We kunnen de tabel maken zoals uitgelegd in AWS Lambda DynamoDB gebruiken met Java.

Laten we kiezen Persoon als tafelnaam, ID kaart als naam van de primaire sleutel, en Aantal als type van de primaire sleutel.

4.7. Code implementeren via AWS-console

Nadat we onze code hebben gebouwd en de tabel hebben gemaakt, kunnen we nu de functies maken en de code uploaden.

Dit kan worden gedaan door stap 1-5 van het AWS Lambda met Java-artikel een keer te herhalen voor elk van onze twee methoden.

Laten we de volgende functienamen gebruiken:

  • StorePersonFunction voor de handleRequest methode (functie 1)
  • GetPersonByHTTPParamFunction voor de handleGetByParam methode (functie 2)

We moeten ook een omgevingsvariabele definiëren TAFEL NAAM met waarde "Persoon".

4.8. De functies testen

Voordat we verder gaan met het daadwerkelijke API Gateway-gedeelte, kunnen we een snelle test uitvoeren in de AWS Console, alleen om te controleren of onze Lambda-functies correct werken en het Proxy Integration-formaat aankunnen.

Het testen van een Lambda-functie vanuit de AWS Console werkt zoals beschreven in het AWS Lambda met Java-artikel.

Echter, wanneer we een testevenement maken, moeten we rekening houden met het speciale proxy-integratieformaat, wat onze functies verwachten. We kunnen ofwel de API Gateway AWS-proxy sjabloon en pas dat aan voor onze behoeften, of we kunnen de volgende gebeurtenissen kopiëren en plakken:

Voor de StorePersonFunction, we zouden dit moeten gebruiken:

{"body": "{\" id \ ": 1, \" naam \ ": \" John Doe \ "}"}

Zoals eerder besproken, is de lichaam moet het type hebben Draad, zelfs als het een JSON-structuur bevat. De reden is dat de API Gateway zijn verzoeken in hetzelfde formaat verzendt.

Het volgende antwoord moet worden geretourneerd:

{"isBase64Encoded": false, "headers": {"x-custom-header": "mijn aangepaste headerwaarde"}, "body": "{\" message \ ": \" Nieuw item gemaakt \ "}", "statusCode": 200}

Hier kunnen we zien dat de lichaam van onze reactie is een Draad, hoewel het een JSON-structuur bevat.

Laten we eens kijken naar de invoer voor de GetPersonByHTTPParamFunction.

Om de functionaliteit van de padparameter te testen, ziet de invoer er als volgt uit:

{"pathParameters": {"id": "1"}}

En de invoer voor het verzenden van een parameter voor een querytekenreeks zou zijn:

{"queryStringParameters": {"id": "1"}}

Als antwoord zouden we voor beide gevallen het volgende moeten krijgen:

{"headers": {"x-custom-header": "mijn aangepaste headerwaarde"}, "body": "{\" Person \ ": {\ n \" id \ ": 88, \ n \" naam \ ": \" John Doe \ "\ n}}", "statusCode": 200}

Nogmaals, de lichaam is een Draad.

5. Maken en testen van de API

Nadat we de Lambda-functies in de vorige sectie hebben gemaakt en geïmplementeerd, we kunnen nu de daadwerkelijke API maken met behulp van de AWS-console.

Laten we eens kijken naar de basisworkflow:

  1. Maak een API aan in ons AWS-account.
  2. Voeg een bron toe aan de bronnenhiërarchie van de API.
  3. Maak een of meer methoden voor de bron.
  4. Zet de integratie op tussen een methode en de bijbehorende Lambda-functie.

We herhalen stap 2-4 voor elk van onze twee functies in de volgende secties.

5.1. Het creëren van de API

Om de API te maken, moeten we:

  1. Meld u aan bij de API Gateway-console op //console.aws.amazon.com/apigateway
  2. Klik op "Aan de slag" en selecteer vervolgens "Nieuwe API"
  3. Typ de naam van onze API (TestAPI) en bevestig door op "API maken" te klikken

Nadat we de API hebben gemaakt, kunnen we nu de API-structuur maken en deze koppelen aan onze Lambda-functies.

5.2. API-structuur voor functie 1

De volgende stappen zijn nodig voor onze StorePersonFunction:

  1. Kies het bovenliggende resource-item onder de "Resources" -boom en selecteer vervolgens "Create Resource" in het vervolgkeuzemenu "Actions". Vervolgens moeten we het volgende doen in het deelvenster "Nieuwe onderliggende bron":
    • Typ "Personen" als een naam in het invoertekstveld "Resource Name"
    • Laat de standaardwaarde staan ​​in het invoertekstveld “Bronpad”
    • Kies 'Bron maken'
  2. Kies de zojuist aangemaakte bron, kies "Aanmaakmethode" in het vervolgkeuzemenu "Acties" en voer de volgende stappen uit:
    • Kies PUT in de vervolgkeuzelijst HTTP-methode en kies vervolgens het vinkje om de keuze op te slaan
    • Laat "Lambda-functie" als integratietype staan ​​en selecteer de optie "Lambda-proxy-integratie gebruiken"
    • Kies de regio uit "Lambda Region", waar we eerder onze Lambda-functies hebben geïmplementeerd
    • Type "StorePersonFunction" in "Lambda-functie"
  3. Kies "Opslaan" en bevestig met "OK" wanneer daarom wordt gevraagd met "Toestemming toevoegen aan Lambda-functie"

5.3. API-structuur voor functie 2 - padparameters

De stappen voor het ophalen van padparameters zijn vergelijkbaar:

  1. Kies de /personen resource-item onder de "Resources" -boom en selecteer vervolgens "Create Resource" in het vervolgkeuzemenu "Actions". Vervolgens moeten we het volgende doen in het deelvenster Nieuwe onderliggende bron:
    • Type "Persoon" als een naam in het invoertekstveld “Resource Name”
    • Wijzig het invoertekstveld "Bronpad" in "{ID kaart}"
    • Kies 'Bron maken'
  2. Kies de zojuist aangemaakte bron, selecteer "Methode maken" in het vervolgkeuzemenu "Acties" en voer de volgende stappen uit:
    • Kies GET in de vervolgkeuzelijst HTTP-methode en kies vervolgens het vinkje om de keuze op te slaan
    • Laat "Lambda-functie" als integratietype staan ​​en selecteer de optie "Lambda-proxy-integratie gebruiken"
    • Kies de regio uit "Lambda Region", waar we eerder onze Lambda-functies hebben geïmplementeerd
    • Type "GetPersonByHTTPParamFunction" in "Lambda-functie"
  3. Kies "Opslaan" en bevestig met "OK" wanneer daarom wordt gevraagd met "Toestemming toevoegen aan Lambda-functie"

Opmerking: het is hier belangrijk om de parameter “Bronpad” in te stellen op "{ID kaart}", Als onze GetPersonByPathParamFunction verwacht dat deze parameter precies zo wordt genoemd.

5.4. API-structuur voor functie 2 - Parameters voor querytekenreeksen

De stappen voor het ontvangen van querystringparameters zijn een beetje anders, zoals we hoeven geen bron te maken, maar moeten in plaats daarvan een queryparameter maken voor de ID kaart parameter:

  1. Kies de /personen resource item onder de "Resources" -boom, selecteer "Create Method" in het "Actions" drop-down menu, en voer de volgende stappen uit:
    • Kies GET in de vervolgkeuzelijst HTTP-methode en selecteer vervolgens het vinkje om de keuze op te slaan
    • Laat "Lambda-functie" als integratietype staan ​​en selecteer de optie "Lambda-proxy-integratie gebruiken"
    • Kies de regio uit "Lambda Region", waar we eerder onze Lambda-functies hebben geïmplementeerd
    • Type "GetPersonByHTTPParamFunction" in "Lambda-functie".
  2. Kies "Opslaan" en bevestig met "OK" wanneer daarom wordt gevraagd met "Toestemming toevoegen aan Lambda-functie"
  3. Kies rechts "Method Request" en voer de volgende stappen uit:
    • Vouw de lijst Parameters voor URL-querytekenreeksen uit
    • Klik op "Queryreeks toevoegen"
    • Type "ID kaart" in het naamveld en kies het vinkje om op te slaan
    • Schakel het selectievakje "Vereist" in
    • Klik op het pensymbool naast “Validatie aanvragen” bovenaan het paneel, selecteer “Valideer parameters en kopteksten van querytekenreeksen” en kies het vinkje

Opmerking: het is belangrijk om de parameter "Query String" in te stellen op "ID kaart", Als onze GetPersonByHTTPParamFunction verwacht dat deze parameter precies zo wordt genoemd.

5.5. Testen van de API

Onze API is nu klaar, maar is nog niet openbaar. Voordat we het publiceren, willen we eerst een snelle test uitvoeren vanuit de console.

Daarvoor kunnen we de respectieve te testen methode selecteren in de "Bronnen" -boom en klikken op de "Test" -knop. Op het volgende scherm kunnen we onze invoer typen, zoals we deze met een client via HTTP zouden verzenden.

Voor StorePersonFunction, moeten we de volgende structuur typen in het veld 'Verzoektekst':

{"id": 2, "name": "Jane Doe"}

Voor de GetPersonByHTTPParamFunction met padparameters moeten we typen 2 als een waarde in het veld "{id}" onder "Pad".

Voor de GetPersonByHTTPParamFunction met parameters van de queryreeks, moeten we typen id = 2 als een waarde in het veld "{personen}" onder "Querystrings".

5.6. Implementatie van de API

Tot nu toe was onze API niet openbaar en daarom alleen beschikbaar via de AWS Console.

Zoals eerder besproken, wanneer we een API implementeren, moeten we deze koppelen aan een fase, wat een soort momentopname van de API is. Als we een API opnieuw implementeren, kunnen we een bestaande fase bijwerken of een nieuwe maken.

Laten we eens kijken hoe het URL-schema voor onze API eruit zal zien:

//{restapi-id}.execute-api.{region}.amazonaws.com/{stageName}

De volgende stappen zijn vereist voor implementatie:

  1. Kies de specifieke API in het navigatievenster "API's"
  2. Kies "Acties" in het navigatiepaneel Bronnen en selecteer "API implementeren" in het vervolgkeuzemenu "Acties"
  3. Kies "[Nieuwe fase]" in de vervolgkeuzelijst "Implementatiefase", typ "test" in "Stage name", en geef optioneel een beschrijving van de stage en implementatie
  4. Activeer de implementatie door 'Implementeren' te kiezen

Na de laatste stap geeft de console de root-URL van de API, bijvoorbeeld //0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test.

5.7. Het aanroepen van het eindpunt

Aangezien de API nu openbaar is, we kunnen het noemen met elke gewenste HTTP-client.

Met krullen, zouden de oproepen er als volgt uitzien.

StorePersonFunction:

curl -X PUT '//0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons' \ -H 'content-type: application / json' \ -d '{"id": 3, "name": "Richard Roe"} '

GetPersonByHTTPParamFunction voor padparameters:

curl -X GET '//0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons/3' \ -H 'content-type: application / json'

GetPersonByHTTPParamFunction voor parameters van de querytekenreeks:

curl -X GET '//0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons?id=3' \ -H 'content-type: application / json'

6. Conclusie

In dit artikel hebben we bekeken hoe we AWS Lambda-functies beschikbaar kunnen maken als REST-endpoints, met behulp van AWS API Gateway.

We hebben de basisconcepten en terminologie van API Gateway onderzocht en we hebben geleerd hoe we Lambda-functies kunnen integreren met Lambda Proxy Integration.

Ten slotte hebben we gezien hoe we een API kunnen maken, implementeren en testen.

Zoals gewoonlijk is alle code voor dit artikel beschikbaar op GitHub.