Napredni REST API dizajn
U jednom od prethodnih tekstova na našem blogu objasnili smo kako da koristite REST API i objasnili neke osnove u vezi sa njim.
U ovom tekstu ćemo objasniti neke primere napredne upotrebe REST API-ja koje možete primeniti u svom svakodnevnom radu.
Takođe, objasnićemo kako da radite sa listama resursa, kao i kako da upravljate greškama i response kodovima.
Rad sa listama: filtriranje, ordering i paginacija
U prethodnom poglavlju smo videli kako da postavimo upit za listu resursa. Ako želimo listu kontakata, na primer, uradili bismo:
GET /contacts
To je dovoljno da sistem vrati listu svih kontakata u bazi podataka. Međutim, ovaj jednostavan primer nije baš praktičan u produkcionim okruženjima jer skup podataka može biti veoma velik i skup za slanje preko HTTP-a, a query operacija neefikasna.
Da bi API pozivi bili što lakši, potrebno je da budemo što precizniji u vezi sa podacima koje želimo da dobijemo. Dakle, možemo filtrirati listu dodavanjem uslova upita i poređati je tako da se najrelevantniji rezultati pojavljuju prvi. Široko rasprostranjen industrijski standard bi bio da se ta lista podeli na nekoliko delova i posluži samo onaj koji je odmah potreban, poput prvih 20 u kolekciji. To se zove paginacija.
Postoji nekoliko načina za prosleđivanje ovih modifikatora na API endpoint. Možemo da koristimo određene URI-je, prilagođena zaglavlja ili parametre upita u URI-ju, na primer.
Hajde da vidimo prednosti i nedostatke svakog od njih.
Specifični URI-ji
Definisanje namenskih endpoint-a za svaku operaciju na listi koju bismo možda želeli da uradimo moglo bi izgledati ovako:
/contacts/filter/:param/:value
/contacts/orderby/:param/:asc
/contacts/page/:pagenumber
Ovo izgleda prilično jednostavno, ali, šta ako želimo da kombinujemo operacije. Mogli bismo imati neku vrstu endpoint-a kao što je:
/contacts/filter/:param/:value/orderby/:param/:asc/page/:pagenumber
Ovde stvari već mogu da počnu da se komplikuju. Šta ako želimo da navedemo nekoliko polja za filtriranje ili da ih poređamo? Morali bismo učiniti endpoint još dužim i težim za debagovanje, održavanje i pamćenje.
Šta ako ne želimo da postavimo sve dostupne parametre ili operacije u datom zahtevu? Morali bismo da ih izostavimo, a server bi morao da pogodi koje su obezbeđene, a koje ne.
Ova alternativa se neće proširivati i zadaće nam više glavobolje, pa hajde da je preskočimo i pređimo na sledeću.
Prilagođena zaglavlja (Custom Headers)
Drugi način za prenošenje operacija i prosleđivanje parametara API-ju je dodavanje prilagođenih zaglavlja u HTTP zahtev.
Na primer:
GET /contactsHeaders:
Query: param1=value,param2=value
Sort: param1:desc,param2:asc
Page: pagenumber,pagesize
Ovo je mnogo lakše za upravljanje i elegantnije rešenje. URI je jednostavan i lak za pamćenje i mi kažemo back-end-u koje operacije želimo da uradimo sa podacima tako što prilažemo uputstva i parametre u zaglavlja, zadržavajući URI nepromenjenim.
Server će proveriti postojanje svakog od zaglavlja i u skladu sa tim primeniti ili izostaviti operaciju. Proširivanje sistema dodatnim poljima je, stoga, veoma lako postići.
Međutim, postoji nekoliko glavnih nedostataka ovog rešenja. Neće se svi klijenti i serveri baviti prilagođenim zaglavljima na isti način. Neki od njih mogu potpuno zanemariti nestandardna zaglavlja. Stoga, ovaj metod može narušiti interoperabilnost sa klijentima.
Prilagođena zaglavlja treba da koristite samo u neformalne svrhe kako klijenti i serveri ne bi bacali grešku kada ih ne pronađu u zahtevu.
Drugi argument protiv ove metode je da su URI jedinstveni identifikatori resursa. Iako se lista kontakata sa stranicama i dalje odnosi na kontakte, ona ima pridruženi modifikator koji treba da bude očigledan i da bude deo URI-ja. Isto važi i za uslove filtriranja i sortiranja.
Da bismo prevazišli ova dva ograničenja, moramo da pogledamo i treći pristup.
Query parametri
Kao što smo videli u prethodna dva odeljka, korišćenje query parametara za promenu URI-ja bila bi najbolja metoda za predstavljanje modifikacija na listi osnovnih resursa. Ovde možete da koristite bilo koju konvenciju imenovanja koja ima više smisla u vašem domenu, ali pokušajte da koristite industrijske standarde da biste olakšali snalaženje vašim API klijentima.
Na primer:
GET /contacts?sortBy=param1:desc,param2&foo=bar&page=3&pagesize=20
Hajde da detaljno pogledamo ovaj endpoint.
Prvo što vidimo je da se root URI-ja nije promenio, on je i dalje /contacts
. Ovo čini API dizajn lakim za pamćenje i upotrebu.
Nakon toga možemo videti različite parametre dodate posle znaka pitanja (?). Query parametri su dovoljno fleksibilni da ih možemo dodati ili izostaviti kako želimo, a lako je proveriti da li su definisani na back-end-u.
URI je jedinstven i specifično identifikuje listu kontakata sortiranih po param1
opadajućem i param2
(podrazumevano rastućim kao uobičajena konvencija), a zatim filtriranu poljem ili atributom pod nazivom foo
čija je vrednost bar
; na kraju, želimo stranicu broj 3 sa veličinom stranice od 20 stavki po stranici, to jest, stavke u rasponu od #41 do #60.
Ovaj metod je lako dizajnirati, implementirati i zapamtiti. Redosled parametara upita neće uticati na rezultat i možemo ga proširiti da bismo dodali onoliko parametara filtera koliko nam je potrebno.
Mogli bismo da tvrdimo da bismo promenom redosleda query parametara proizveli dupli URI – što je pogrešno po definiciji jer URI moraju biti jedinstveni. Međutim, to bi API učinilo nefleksibilnim, tako da ćemo morati da prihvatimo taj kompromis.
Upravljanje greškama i response kodovi
Većinu vremena, traženje podataka od API-ja će vratiti podatke koje smo tražili na način koji smo naveli. Ali često se dešavaju greške i back-end ne može da obradi zahtev iz bilo kog razloga.
Važno je da API obrađuje ove izuzetke i vraća odgovarajuću poruku o grešci kako bi klijent znao šta da radi, npr. da li da zahteva ponovo, ili da popravi zahtev, ili da sačeka neko vreme.
Postoji cela lista status kodova koje vaš API može da vrati sa odgovorom. Oni su u osnovi grupisani u sledeće kategorije:
- 1xx: informational
- 2xx: success
- 3xx: redirection
- 4xx: client error
- 5xx: server error
Evo spiska onih ne na koje ćete češće nailaziti:
- 200 OK: server je dobio zahtev i odgovorio u skladu sa tim.
- 201 Created: kreiran je novi resurs; trebalo bi da vratite ovaj kod kada na primer upravljate POST radnjom na kolekciji.
- 202 Accepted: zahtev je prihvaćen, ali nije obrađen; nalazi se na čekanju za izvršenje i možda neće biti ispunjen. Ovaj kod možete da koristite kada zahtev generiše radnju koja će se desiti asinhrono, poput ažuriranja podataka ili e-mail.
- 204 No Content: zahtev je obrađen, ali server nema sadržaj za vraćanje. Ovaj kod možete koristiti kada rukujete DELETE zahtevima.
- 301 Moved Permanently: URL se trajno promenio i može se naći na drugom mestu.
- 302 Found & 303 See Other: URL se privremeno promenio i može se naći na drugom mestu.
- 304 Not Modified: koristite ovaj kod za implementaciju sistema za keširanje. Ako se koriste zaglavlja zahteva
If-Modified-Since
iliIf-None-Match
, a server nema noviju verziju koju treba da obezbedi, vratite 304 tako da klijent može da koristi kopiju koju je lokalno uskladištio. - 400 Bad Request: zahtev je pogrešno formiran. Koristite ovaj kod da kažete klijentu da ponovo pošalje informacije koje ispravljaju problem sa formatom.
- 401 Unauthorized: korisnik nije autentifikovan i ne može mu se odobriti pristup ograničenom resursu. Prijavljivanje na sistem će rešiti ovu grešku.
- 403 Forbidden: korisnik je autentifikovan, ali ne može da pristupi ovom resursu. Prijavljivanje na sistem neće promeniti ovu grešku.
- 404 Not Found: resurs nije pronađen. verovatno najpoznatiji kod greške na Internetu.
- 405 Method Not Allowed: koristite ovaj kod kada klijent zahteva HTTP koji dati resurs ne može da obradi, kao što je POST na pojedinačnim resursima ili PUT na kolekcijama.
- 415 Unsupported Media Type: koristite ovaj kod kada vaš API ne podržava tip sadržaja koji koristi ili zahteva klijent, kao što je JSON kada biste očekivali XML.
- 500 Internal Server Error: neočekivana greška je sprečila server da vrati odgovor.
- 503 Service Unavailable: server je privremeno nedostupan zbog preopterećenosti ili zbog održavanja.
Trebalo bi da se uverimo da su pravi HTTP kodovi vraćeni zajedno sa odgovorom, bilo da je to bilo uspešno ili neuspešno. Ovo će pomoći vašim korisnicima API-ja da razumeju informacije koje dobijaju iz vašeg sistema i da na osnovu toga naprave svoje aplikacije.
Takođe će olakšati otklanjanje grešaka i testiranje vašeg back-end-a – možda ćete brže pronaći neke marginalne slučajeve ako pogledate kodove vraćenih grešaka.
Još jedna važna najbolja praksa je da vratite eksplicitnu i smislenu error poruku, sa vezom do daljeg objašnjenja problema, tako da programeri koji koriste vaš API mogu da nauče kako da ga pravilno koriste.
Takođe, razmislite o dodavanju zaglavlja datuma sa vremenom kada je greška nastala i ID greške, za dalju referencu. Na primer:
HTTP/1.1 400 Bad Request
Date: Wed, 4 Jan 2017 20:41 GMT
Link: <https://www.nekidomen.rs/errors/badrequest.html>;rel="help"{
"message": "Wrong input parameters. Missing value for 'sortBy'",
"errorId": "348–587–956"
}
Trebalo bi da pokušate da poruku o grešci učinite što jasnijom kako bi vašim korisnicima API-ja bilo lakše da naprave svoje aplikacije na vrhu vašeg sistema.
Kreiranje verzija API-ja
API je kapija našeg sistema u svet. Mnoge spoljne aplikacije, sistemi i usluge mogu zavisiti od toga. Kao rezultat toga, kada API postane javan, ne bi trebalo da se menja: njegovi endpoint-i i strukture podataka odgovora treba da budu konzistentne.
Softverski sistemi treba da budu dizajnirani imajući na umu mogućnost jednostavnog održavanja i trebalo bi da se razvijaju i menjaju veoma brzo u savremenom svetu weba u cloud-u. Kada vaša aplikacija treba da se promeni tako da ne može da održi kompatibilnost ili neki klijenti zahtevaju drugačije ponašanje od drugih klijenata, pravo je vreme za novu verziju vašeg API-ja.
Najčešće korišćeni obrazac je prefiks vaše URI šeme sa lako uočljivim terminima kao što su v1 ili v2 u imenima poddomena, segmentima putanje ili query parametrima. Ovo su neke opcije:
https://v1.api.rs/contacts
https://api.rs/v2/contacts
https://api.rs/contacts?version=v1
Koristite template koji najbolje funkcioniše sa vašom implementacijom servera i framework-ovima.
Ako želite da ista aplikacija rukuje različitim verzijama API-ja, korišćenje segmenata putanje ili parametara upita može biti dobra ideja.
Uzmite u obzir i da kreiranje verzija API-ja može dovesti do drugih problema, kao što su:
- Kompatibilnost podataka može da se prekine između verzija ili prilikom nadogradnje na poslednju verziju.
- Različita biznis pravila i tokovi aplikacija mogu zahtevati ažuriranje klijenata.
- Održavanje više verzija iste aplikacije će dovesti do dodatnog usložnjavanja vašeg sistema.
- Klijenti će morati da budu ažurirani da bi koristili nove endpoint-e API-ja.
Jednom kada kreirate novu verziju API-ja, ta verzija treba da bude nepromenljiva i sve buduće promene i prilagođavanja treba da budu objavljene u novoj verziji.
Ovo je od vitalnog značaja da vaši API klijenti nastave da rade ispravno.
Bez komentara