Korištenje AWS Lambda s API pristupnikom
1. Pregled
AWS Lambda je računalna usluga bez poslužitelja koju pruža Amazon Web Services.
U dva prethodna članka raspravljali smo o tome kako stvoriti AWS Lambda funkciju pomoću Jave, kao i o pristupu DynamoDB iz Lambda funkcije.
U ovom uputstvu ćemo razgovarati kako objaviti Lambda funkciju kao REST krajnju točku, koristeći AWS Gateway.
Detaljno ćemo pogledati sljedeće teme:
- Osnovni pojmovi i pojmovi API Gateway-a
- Integracija Lambda funkcija s API pristupnikom pomoću integracije Lambda Proxy
- Izrada API-ja, njegova struktura i kako mapirati API resurse na Lambda funkcije
- Uvođenje i ispitivanje API-ja
2. Osnove i pojmovi
API Gateway je potpuno upravljana usluga koja programerima omogućuje stvaranje, objavljivanje, održavanje, nadgledanje i zaštitu API-ja u bilo kojem mjerilu.
Možemo implementirati dosljedno i skalabilno programsko sučelje temeljeno na HTTP-u (koje se nazivaju i RESTful usluge) za pristup pozadinskim uslugama poput Lambda funkcija, daljnjih AWS usluga (npr. EC2, S3, DynamoDB) i bilo kojih HTTP krajnjih točaka.
Značajke uključuju, ali nisu ograničene na:
- Upravljanje prometom
- Ovlaštenje i kontrola pristupa
- Praćenje
- Upravljanje inačicama API-ja
- Umanjivanje zahtjeva za sprečavanje napada
Kao i AWS Lambda, API pristupnik se automatski smanjuje i naplaćuje po API pozivu.
Detaljne informacije mogu se naći u službenoj dokumentaciji.
2.1. Pojmovi
API pristupnik je AWS usluga koja podržava stvaranje, postavljanje i upravljanje sučeljem aplikacijskog programiranja RESTful za izlaganje pozadinskih HTTP krajnjih točaka, AWS Lambda funkcija i drugih AWS usluga.
An API API pristupnika je zbirka resursa i metoda koje se mogu integrirati s Lambda funkcijama, drugim AWS uslugama ili HTTP krajnjim točkama u pozadini. API se sastoji od resursa koji čine strukturu API-ja. Svaki API resurs može izložiti jednu ili više API metoda koje moraju imati jedinstvene HTTP glagole.
Da bismo objavili API, moramo stvoriti Uvođenje API-ja i povezati ga s tzv pozornica. Faza je poput snimke u vremenu API-ja. Ako preusmjerimo API, možemo ili ažurirati postojeću fazu ili stvoriti novu. Po tome su moguće različite verzije API-ja istodobno, na primjer a razv pozornica, a test pozornici, pa čak i više produkcijskih verzija, poput v1, v2itd.
Integracija Lambda Proxyja je pojednostavljena konfiguracija za integraciju između Lambda funkcija i API Gatewaya.
API pristupnik šalje cijeli zahtjev kao ulaz u pozadinsku Lambda funkciju. Reagirajući, API pristupnik transformira izlaz Lambda funkcije natrag u sučelni HTTP odgovor.
3. Ovisnosti
Trebat će nam iste ovisnosti kao u članku AWS Lambda koristeći DynamoDB s Javom.
Povrh toga, trebamo i JSON Simple knjižnicu:
com.googlecode.json-simple json-simple 1.1.1
4. Razvoj i primjena Lambda funkcija
U ovom ćemo odjeljku razviti i izgraditi svoje Lambda funkcije u Javi, rasporedit ćemo ih pomoću AWS Console i pokrenuti ćemo brzi test.
Kako želimo pokazati osnovne mogućnosti integracije API Gateway-a s Lambda, stvorit ćemo dvije funkcije:
- Funkcija 1: prima korisni teret od API-ja, koristeći PUT metodu
- Funkcija 2: pokazuje kako se koristi parametar HTTP puta ili parametar HTTP upita koji dolaze iz API-ja
Što se tiče implementacije, mi ćemo je stvoriti RequestHandler klase, koja ima dvije metode - po jednu za svaku funkciju.
4.1. Model
Prije nego što implementiramo stvarni rukovatelj zahtjevima, hajde da kratko pogledamo naš model podataka:
javna klasa Osoba {private int id; privatni naziv niza; javna osoba (niz json) {Gson gson = novi Gson (); Zahtjev za osobom = gson.fromJson (json, Person.class); this.id = request.getId (); this.name = request.getName (); } javni String toString () {Gson gson = novi GsonBuilder (). setPrettyPrinting (). create (); return gson.toJson (ovo); } // geteri i postavljači}
Naš se model sastoji od jednog jednostavnog Osoba klase, koja ima dva svojstva. Jedini zapaženi dio je Osoba (niz) konstruktor, koji prihvaća JSON String.
4.2. Implementacija klase RequestHandler
Baš kao u članku AWS Lambda With Java, izradit ćemo implementaciju RequestStreamHandler sučelje:
javna klasa APIDemoHandler implementira RequestStreamHandler {private static final String DYNAMODB_TABLE_NAME = System.getenv ("TABLE_NAME"); @Override public void handleRequest (InputStream inputStream, OutputStream outputStream, Context context) baca IOException {// implementacija} public void handleGetByParam (InputStream inputStream, OutputStream outputStream, Context context) baca IOException {// implementacija}}
Kao što vidimo, RequestStreamHander sučelje definira samo jednu metodu, handeRequest (). U svakom slučaju, možemo definirati daljnje funkcije u istoj klasi, kao što smo to učinili ovdje. Druga opcija bila bi stvaranje jedne implementacije RequestStreamHander za svaku funkciju.
U našem konkretnom slučaju prvoga smo odabrali zbog jednostavnosti. Međutim, izbor se mora donositi od slučaja do slučaja, uzimajući u obzir čimbenike kao što su izvedba i održivost koda.
Također smo pročitali naziv naše DynamoDB tablice s TABLE_NAME varijabla okoline. Tu ćemo varijablu definirati kasnije tijekom implementacije.
4.3. Provedba funkcije 1
U našoj prvoj funkciji želimo pokazati kako dobiti korisni teret (na primjer iz zahtjeva PUT ili POST) s API pristupnika:
public void handleRequest (InputStream inputStream, OutputStream outputStream, Context context) baca IOException {JSONParser parser = new JSONParser (); Čitač BufferedReader = novi BufferedReader (novi InputStreamReader (inputStream)); JSONObject responseJson = novi JSONObject (); AmazonDynamoDB klijent = AmazonDynamoDBClientBuilder.defaultClient (); DynamoDB dynamoDb = novi DynamoDB (klijent); isprobajte {JSONObject event = (JSONObject) parser.parse (čitač); if (event.get ("body")! = null) {Person person = new Person ((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 = novi JSONObject (); responseBody.put ("poruka", "Nova stavka stvorena"); JSONObject headerJson = novi JSONObject (); headerJson.put ("x-custom-header", "moja vrijednost prilagođenog zaglavlja"); responseJson.put ("statusCode", 200); responseJson.put ("zaglavlja", headerJson); responseJson.put ("body", responseBody.toString ()); } catch (ParseException pex) {responseJson.put ("statusCode", 400); responseJson.put ("iznimka", pex); } OutputStreamWriter Writer = novi OutputStreamWriter (outputStream, "UTF-8"); Writer.Write (responseJson.toString ()); pisac.close (); }
Kao što je već spomenuto, API ćemo konfigurirati kasnije da koristi Lambda proxy integraciju. Očekujemo da API pristupnik proslijedi cjeloviti zahtjev funkciji Lambda u InputStream parametar.
Sve što moramo učiniti je odabrati relevantne atribute iz sadržane JSON strukture.
Kao što vidimo, metoda se u osnovi sastoji od tri koraka:
- Dohvaćanje tijelo objekt iz našeg ulaznog toka i stvaranje a Osoba objekt iz toga
- Pohranjujući to Osoba objekt u DynamoDB tablici
- Izgradnja JSON objekta koji može sadržavati nekoliko atributa, poput tijelo za odgovor, prilagođena zaglavlja, kao i HTTP statusni kod
Ovdje valja spomenuti jednu točku: API Gateway očekuje tijelo biti Niz (i za zahtjev i za odgovor).
Kao što očekujemo da ćemo dobiti Niz kao tijelo iz API Gateway-a, emitirali smo tijelo do Niz i inicijalizirati naš Osoba objekt:
Osoba osoba = nova osoba Person ((Niz) event.get ("body"));
API Gateway također očekuje odgovor tijelo biti Niz:
responseJson.put ("body", responseBody.toString ());
Ova tema nije izričito navedena u službenoj dokumentaciji. Međutim, ako pažljivo pogledamo, možemo vidjeti da je atribut body a Niz u oba isječka za zahtjev kao i za odgovor.
Prednost bi trebala biti jasna: čak i ako je JSON format između API Gateway-a i Lambda funkcije, stvarno tijelo može sadržavati običan tekst, JSON, XML ili bilo što drugo. Tada je odgovornost funkcije Lambda za pravilno rukovanje formatom.
Kako će izgledati tijelo zahtjeva i odgovora vidjet ćemo kasnije kada testiramo svoje funkcije u AWS konzoli.
Isto se odnosi i na sljedeće dvije funkcije.
4.4. Provedba funkcije 2
U drugom koraku želimo pokazati kako koristiti parametar puta ili parametar niza upita za dohvaćanje a Osoba stavka iz baze podataka koristeći njezin ID:
javna void handleGetByParam (InputStream inputStream, OutputStream outputStream, Context context) baca IOException {JSONParser parser = new JSONParser (); Čitač BufferedReader = novi BufferedReader (novi InputStreamReader (inputStream)); JSONObject responseJson = novi JSONObject (); AmazonDynamoDB klijent = AmazonDynamoDBClientBuilder.defaultClient (); DynamoDB dynamoDb = novi DynamoDB (klijent); Rezultat stavke = null; isprobajte {JSONObject event = (JSONObject) parser.parse (čitač); JSONObject responseBody = novi 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")); rezultat = 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")); rezultat = dynamoDb.getTable (DYNAMODB_TABLE_NAME) .getItem ("id", id); }} if (rezultat! = null) {Osoba osoba = nova Osoba (rezultat.toJSON ()); responseBody.put ("Osoba", osoba); responseJson.put ("statusCode", 200); } else {responseBody.put ("poruka", "Nije pronađena nijedna stavka"); responseJson.put ("statusCode", 404); } JSONObject headerJson = novi JSONObject (); headerJson.put ("x-custom-header", "moja vrijednost prilagođenog zaglavlja"); responseJson.put ("zaglavlja", headerJson); responseJson.put ("body", responseBody.toString ()); } catch (ParseException pex) {responseJson.put ("statusCode", 400); responseJson.put ("iznimka", pex); } OutputStreamWriter Writer = novi OutputStreamWriter (outputStream, "UTF-8"); Writer.Write (responseJson.toString ()); pisac.close (); }
Opet su važna tri koraka:
- Provjeravamo je li a pathParameters ili an queryStringParameters niz s iskaznica atribut su prisutni.
- Ako pravi, koristimo pripadajuću vrijednost da bismo zatražili a Osoba stavka s tim ID-om iz baze podataka.
- Odgovoru dodamo JSON prikaz primljene stavke.
Službena dokumentacija pruža detaljnije objašnjenje ulaznog i izlaznog formata za Proxy Integration.
4.5. Građevinski kod
Opet, možemo jednostavno izgraditi naš kod pomoću Mavena:
mvn čisti paket nijansa: hlad
JAR datoteka će se stvoriti pod cilj mapu.
4.6. Izrada DynamoDB tablice
Tablicu možemo stvoriti kako je objašnjeno u AWS Lambda koristeći DynamoDB s Javom.
Birajmo Osoba kao naziv tablice, iskaznica kao naziv primarnog ključa i Broj kao tip primarnog ključa.
4.7. Postavljanje koda putem AWS konzole
Nakon izgradnje našeg koda i izrade tablice, sada možemo stvoriti funkcije i prenijeti kôd.
To se može učiniti ponavljanjem koraka 1-5 iz članka AWS Lambda s Javom, jednom za svaku od naše dvije metode.
Upotrijebimo sljedeća imena funkcija:
- StorePersonFunction za handleRequest metoda (funkcija 1)
- GetPersonByHTTPParamFunction za handleGetByParam metoda (funkcija 2)
Također moramo definirati varijablu okruženja TABLE_NAME s vrijednošću "Osoba".
4.8. Testiranje funkcija
Prije nastavka sa stvarnim dijelom API Gateway-a, možemo pokrenuti brzi test u AWS konzoli, samo da provjerimo rade li naše Lambda funkcije ispravno i mogu li se nositi s formatom Proxy Integration.
Testiranje Lambda funkcije iz AWS konzole djeluje kako je opisano u članku AWS Lambda s Javom.
Međutim, kada kreiramo testni događaj, moramo uzeti u obzir poseban format Proxy Integration, koje naše funkcije očekuju. Možemo koristiti API pristupnik AWS proxy predložak i prilagodite ga našim potrebama ili možemo kopirati i zalijepiti sljedeće događaje:
Za StorePersonFunction, trebali bismo koristiti ovo:
{"body": "{\" id \ ": 1, \" name \ ": \" John Doe \ "}"}
Kao što je već spomenuto, tijelo mora imati tip Niz, čak i ako sadrži JSON strukturu. Razlog je taj što će API pristupnik svoje zahtjeve slati u istom formatu.
Treba vratiti sljedeći odgovor:
{"isBase64Encoded": false, "headers": {"x-custom-header": "my custom header value"}, "body": "{\" message \ ": \" Nova stavka stvorena \ "}", "statusCode": 200}
Ovdje možemo vidjeti da je tijelo našeg odgovora je a Niz, iako sadrži JSON strukturu.
Pogledajmo ulaz za GetPersonByHTTPParamFunction.
Za testiranje funkcionalnosti parametra puta ulaz bi izgledao ovako:
{"pathParameters": {"id": "1"}}
A ulaz za slanje parametra niza upita bio bi:
{"queryStringParameters": {"id": "1"}}
Kao odgovor trebali bismo dobiti sljedeće za oba slučaja:
{"headers": {"x-custom-header": "my custom header value"}, "body": "{\" Person \ ": {\ n \" id \ ": 88, \ n \" naziv \ ": \" John Doe \ "\ n}}", "statusCode": 200}
Opet, tijelo je Niz.
5. Stvaranje i testiranje API-ja
Nakon što smo stvorili i primijenili Lambda funkcije u prethodnom odjeljku, sada možemo stvoriti stvarni API pomoću AWS konzole.
Pogledajmo osnovni tijek rada:
- Stvorite API na našem AWS računu.
- Dodajte resurs u hijerarhiju resursa API-ja.
- Stvorite jednu ili više metoda za resurs.
- Postavite integraciju između metode i pripadajuće Lambda funkcije.
Ponovit ćemo korake 2-4 za svaku od naše dvije funkcije u sljedećim odjeljcima.
5.1. Izrada API-ja
Za stvaranje API-a morat ćemo:
- Prijavite se na konzolu API Gateway na //console.aws.amazon.com/apigateway
- Kliknite "Započni", a zatim odaberite "Novi API"
- Upišite ime našeg API-ja (TestAPI) i potvrdite klikom na "Stvori API"
Nakon što smo kreirali API, sada možemo stvoriti strukturu API-ja i povezati je s našim Lambda funkcijama.
5.2. API struktura za funkciju 1
Sljedeći koraci su neophodni za naše StorePersonFunction:
- Odaberite nadređenu stavku resursa ispod stabla "Resursi", a zatim na padajućem izborniku "Akcije" odaberite "Stvori resurs". Zatim u oknu "New Child Resource" moramo učiniti sljedeće:
- Unesite "Osobe" kao ime u polje za tekst za unos "Naziv resursa"
- Ostavite zadanu vrijednost u polju za unos teksta "Put resursa"
- Odaberite "Stvori resurs"
- Odaberite upravo stvoreni resurs, odaberite "Stvori metodu" s padajućeg izbornika "Akcije" i izvedite sljedeće korake:
- Odaberite PUT s padajućeg popisa metode HTTP, a zatim odaberite ikonu kvačice da biste spremili izbor
- Ostavite "Lambda funkciju" kao vrstu integracije i odaberite opciju "Koristi integraciju Lambda Proxy"
- Odaberite regiju iz "Lambda regije", u kojoj smo prije implementirali svoje Lambda funkcije
- Tip "StorePersonFunction" u "Lambda funkciji"
- Odaberite "Spremi" i potvrdite s "U redu" kada se zatraži "Dodaj dopuštenje za funkciju Lambda"
5.3. API struktura za funkciju 2 - Parametri puta
Koraci za naše parametre dohvaćanja puta su slični:
- Odaberi /osobe stavku resursa ispod stabla „Resursi“, a zatim na padajućem izborniku „Akcije“ odaberite „Stvori resurs“. Zatim u oknu New Child Resource moramo učiniti sljedeće:
- Tip "Osoba" kao ime u tekstnom polju za unos "Naziv resursa"
- Promijenite polje za tekst "Put resursa" u "{iskaznica}"
- Odaberite "Stvori resurs"
- Odaberite upravo stvoreni resurs, na padajućem izborniku "Akcije" odaberite "Način izrade" i izvedite sljedeće korake:
- Odaberite GET s padajućeg popisa metode HTTP, a zatim odaberite ikonu kvačice da biste spremili izbor
- Ostavite "Lambda funkciju" kao vrstu integracije i odaberite opciju "Koristi integraciju Lambda Proxy"
- Odaberite regiju iz "Lambda regije", u kojoj smo prije implementirali svoje Lambda funkcije
- Tip “GetPersonByHTTPParamFunction” u "Lambda funkciji"
- Odaberite "Spremi" i potvrdite s "U redu" kada se zatraži "Dodaj dopuštenje za funkciju Lambda"
Napomena: ovdje je važno postaviti parametar "Put resursa" na "{iskaznica}", kao naš GetPersonByPathParamFunction očekuje da će ovaj parametar biti imenovan upravo ovako.
5.4. API struktura za funkciju 2 - Parametri niza upita
Koraci za primanje parametara niza upita su malo drugačiji, kao ne moramo stvoriti resurs, već umjesto toga moramo stvoriti parametar upita za iskaznica parametar:
- Odaberi /osobe stavku resursa ispod stabla „Resursi“, na padajućem izborniku „Akcije“ odaberite „Način izrade“ i izvedite sljedeće korake:
- Odaberite GET s padajućeg popisa metode HTTP, a zatim odaberite ikonu kvačice da biste spremili izbor
- Ostavite "Lambda funkciju" kao vrstu integracije i odaberite opciju "Koristi integraciju Lambda Proxy"
- Odaberite regiju iz "Lambda regije", u kojoj smo prije implementirali svoje Lambda funkcije
- Tip “GetPersonByHTTPParamFunction” u “Lambda funkciji”.
- Odaberite "Spremi" i potvrdite s "U redu" kada se zatraži "Dodaj dopuštenje za funkciju Lambda"
- Odaberite "Zahtjev za metodom" s desne strane i poduzmite sljedeće korake:
- Proširite popis parametara niza URL-a upita
- Kliknite "Dodaj niz upita"
- Tip "iskaznica" u polju za ime i odaberite ikonu kvačice za spremanje
- Označite potvrdni okvir "Obavezno"
- Kliknite simbol olovke pored "Zahtjev za provjeru valjanosti" na vrhu ploče, odaberite "Provjeri parametre niza upita i zaglavlja" i odaberite ikonu kvačice
Napomena: Važno je postaviti parametar "Niz upita" na "iskaznica", kao naš GetPersonByHTTPParamFunction očekuje da će ovaj parametar biti imenovan upravo ovako.
5.5. Testiranje API-ja
Naš API je sada spreman, ali još nije javan. Prije nego što ga objavimo, prvo želimo pokrenuti brzi test s konzole.
Za to možemo odabrati odgovarajuću metodu koja će se testirati na stablu „Resursi“ i kliknuti na gumb „Test“. Na sljedećem zaslonu možemo unijeti svoj ulaz, jer bismo ga poslali s klijentom putem HTTP-a.
Za StorePersonFunction, u polje "Tijelo zahtjeva" moramo upisati sljedeću strukturu:
{"id": 2, "name": "Jane Doe"}
Za GetPersonByHTTPParamFunction s parametrima puta, moramo tipkati 2 kao vrijednost u polje "{id}" pod "Put".
Za GetPersonByHTTPParamFunction s parametrima niza upita moramo tipkati id = 2 kao vrijednost u polje „{persons}“ u odjeljku „Nizovi upita“.
5.6. Uvođenje API-ja
Do sada naš API nije bio javan i stoga je bio dostupan samo s AWS konzole.
Kao što je već spomenuto, kad rasporedimo API, moramo ga povezati sa fazom, koja je poput snimke u vremenu API-ja. Ako preusmjerimo API, možemo ili ažurirati postojeću fazu ili stvoriti novu.
Pogledajmo kako će izgledati shema URL-a za naš API:
//{restapi-id}.execute-api.{region}.amazonaws.com/{stageName}
Za implementaciju su potrebni sljedeći koraci:
- Odaberite određeni API u navigacijskom oknu "API-ji"
- Odaberite "Akcije" u navigacijskom oknu Resursi i odaberite "Implementiraj API" s padajućeg izbornika "Akcije"
- Odaberite "[Nova faza]" s padajućeg izbornika "Faza implementacije", upišite "test" u "Naziv faze" i po želji pružiti opis faze i raspoređivanja
- Pokrenite implementaciju odabirom "Rasporedi"
Nakon zadnjeg koraka, konzola će pružiti korijenski URL API-ja, na primjer, //0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test.
5.7. Pozivanje na krajnju točku
Kako je API sada javan, možemo ga nazvati pomoću bilo kojeg HTTP klijenta kojeg želimo.
S kovrča, pozivi bi izgledali kako slijedi.
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 za parametre puta:
curl -X GET '//0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons/3' \ -H 'vrsta sadržaja: application / json'
GetPersonByHTTPParamFunction za parametre niza upita:
curl -X GET '//0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons?id=3' \ -H 'vrsta sadržaja: application / json'
6. Zaključak
U ovom smo članku pogledali kako učiniti AWS Lambda funkcije dostupnim kao REST krajnje točke, koristeći AWS API Gateway.
Istražili smo osnovne koncepte i terminologiju API Gateway-a i naučili kako integrirati Lambda funkcije pomoću Lambda Proxy Integration.
Napokon smo vidjeli kako stvoriti, implementirati i testirati API.
Kao i obično, sav kôd za ovaj članak dostupan je na GitHubu.