Onion Information
Implementacija REpresentational State Transfer (REST) aplikacijskog programskog sučelja u jeziku PHP - GASERI
REpresentational State Transfer (REST) je danas najkorištenija softverska arhitektura koja koristi HTTP i namijenjena je za aplikacije temeljene na web servisima (više detalja o REST-u na MDN-u). Za web servis koji implementira REST kažemo ...
Onion Details
Page Clicks: 0
First Seen: 03/15/2024
Last Indexed: 10/23/2024
Onion Content
Preskoči na sadržaj Implementacija REpresentational State Transfer (REST) aplikacijskog programskog sučelja u jeziku PHP REpresentational State Transfer (REST) je danas najkorištenija softverska arhitektura koja koristi HTTP i namijenjena je za aplikacije temeljene na web servisima ( više detalja o REST-u na MDN-u ). Za web servis koji implementira REST kažemo da je RESTful ; mi ćemo se u nastavku baviti takvim servisima, a aplikacijama koje se na njima temelje kasnije na predmetima iz područja razvoja web aplikacija. RESTful web servisi su postali u praksi jako zastupljeni tijekom 2010-ih . REST je razvio Roy Fielding 2000. godine u svom doktoratu i pritom kaže da se njegov razvoj temelji na HTTP-ovom modelu objekta iz 1994. godine (više detalja se može naći u poglavlju 5 pod naslovom Representational State Transfer (REST) i poglavlju 6 pod naslovom Experience and Evaluation ). Podsjetimo se da HTTP odredište zahtjeva naziva resursom i pritom ne definira detaljnije što resurs može biti ( više detalja o identificiranju resursa na webu može se pronaći na MDN-u ). Kako REST koristi HTTP, on specijalno koristi i pojam resursa iz HTTP-a. Ključna apstrakcija podataka u REST-u je resurs; on može biti slika, dokument, zvuk, reprezentacija objekta iz stvarnosti (npr. osobe ili institucije), zbirka drugih resursa i slično. Stanje resursa u bilo kojem trenutku naziva se reprezentacija; ona sadrži podatke, metapodatke i poveznice prema drugim resursima. Za navođenje oblika reprezentacije koristi se već ranije spomenuti MIME tip. Resursi se identificiraju korištenjem Uniform Resource Identifiera (URI) . Recimo, možemo imati RESTful web servis koji putem URI-ja /persons ( http://localhost:8000/persons ) korištenjem HTTP metode GET omogućuje dohvaćanje popisa svih osoba koji je oblika: { "count" : 2 , "results" : [ { "name" : "Dennis MacAlistair Ritchie" , "birth_year" : 1941 , "known_for" : [ "http://localhost:8000/technologies/1" , "http://localhost:8000/technologies/2" ], "created" : "2020-12-09T14:50:32+0100" , "edited" : "2020-12-20T21:14:07+0100" , "url" : "http://localhost:8000/persons/1" }, { "name" : "Kenneth Lane Thompson" , "birth_year" : 1943 , "known_for" : [ "http://localhost:8000/technologies/2" ], "created" : "2020-12-10T15:10:41+0100" , "edited" : "2020-12-20T21:17:55+0100" , "url" : "http://localhost:8000/persons/2" } ] } Za usporedbu, putem URI-ja /persons/1 može se dohvatiti osoba s identifikatorom 1 i primljeni odgovor će biti oblika: { "name" : "Dennis MacAlistair Ritchie" , "birth_year" : 1941 , "known_for" : [ "http://localhost:8000/technologies/1" , "http://localhost:8000/technologies/2" ], "created" : "2020-12-09T14:50:32+0100" , "edited" : "2020-12-20T21:14:07+0100" , "url" : "http://localhost:8000/persons/1" } Napomene radi, tehnologija s identifikatorom 1 je programski jezik C , a tehnologija s identifikatorom 2 je Unix . Njihove reprezentacije možete zamisliti po želji. U nastavku ćemo implementirati web servis koji na upite na te URI-je vraća odgovore s navedenim sadržajima i postavlja njihov MIME tip na application/json . Naravno, pored čitanja podataka, naš će web servis omogućavati i druge radnje nad podacima kao što su stvaranje, osvježavanje i brisanje (engl. create, read, update, and delete , kraće CRUD; više detalja o CRUD-u na MDN-u ). Radnje create, read, update i delete (CRUD) i pripadne HTTP metode Korištenjem HTTP metoda i URI-ja možemo izvesti sve četiri navedene radnje nad podacima: stvaranje (engl. create ) vrši se: HTTP metodom POST na URI /persons stvara se osoba s prvim slobodnim identifikatorom ili HTTP metodom PUT na URI /persons/{id} , pri čemu je {id} dotad neiskorišten identifikator osobe, stvara se nova osoba s navedenim identifikatorom čitanje (engl. read ) vrši se: HTTP metodom GET na URI /persons dohvaćaju se sve osobe ili HTTP metodom GET na URI /persons/{id} , pri čemu je {id} identifikator osobe, dohvaća se osoba s navedenim identifikatorom osvježavanje (engl. update ) vrši se: HTTP metodom PUT na URI /persons/{id} , pri čemu je {id} identifikator osobe, osvježava se čitava osoba ili HTTP metodom PATCH na URI /persons/{id} , pri čemu je {id} identifikator osobe, osvježavaju se dijelovi osobe brisanje (engl. delete ) vrši se HTTP metodom DELETE na URI /persons/{id} , pri čemu je {id} identifikator osobe REST ne definira način pohrane podataka i prepušta to implementaciji. Podaci mogu biti pohranjeni u relacijskoj bazi, nerelacijskoj bazi ili datoteci, a mi ćemo ih pohranjivati u datoteci radi jednostavnosti. Note Implementacija koju koristimo u nastavku ne koristi gotove komponente (npr. Symfony ) i okvire (npr. Lumen ) jer je naš cilj ovdje razumijeti kako radi HTTP i kako implementirati REST, a ne razviti veliku praktično primjenjivu aplikaciju koju je kasnije potrebno održavati i proširivati. Primjer sličnog pristupa tumačenju pojmova u domeni weba je Shopifyjev članak How to Build a Web App with and without Rails Libraries u okviru kojeg je dan prikaz implementacije aplikacije u jeziku Ruby bez korištenja okvira Rails . Čitanje podataka Recimo da u istom direktoriju public gdje se nalazi index.php postoji datoteka persons.json sadržaja: { "1" : { "name" : "Dennis MacAlistair Ritchie" , "birth_year" : 1941 , "known_for" : [ "http://localhost:8000/technologies/1" , "http://localhost:8000/technologies/2" ], "created" : "2020-12-09T14:50:32+0100" , "edited" : "2020-12-20T21:14:07+0100" , "url" : "http://localhost:8000/persons/1" }, "2" : { "name" : "Kenneth Lane Thompson" , "birth_year" : 1943 , "known_for" : [ "http://localhost:8000/technologies/2" ], "created" : "2020-12-10T15:10:41+0100" , "edited" : "2020-12-20T21:17:55+0100" , "url" : "http://localhost:8000/persons/2" } Warning Iz sigurnosne perspektive, spremanje podataka u datoteku koja se nalazi na mjestu s kojeg je klijenti mogu dohvatiti HTTP zahtjevima (u ovom slučaju naredbom curl http://localhost:8000/persons.json ) je katastrofalno loša praksa. U procesu učenja datoteku pohranjujemo na tom mjestu samo radi jednostavnosti. Mi, naravno, ne možemo biti sigurni da će ta datoteka uvijek postojati pa ćemo kod pokretanja funkcijom file_exists() ( dokumentacija ) provjeriti njeno postojanje. Ako postoji, uzet ćemo da ona ima spremljene podatke od ranije te je učitati funkcijom file_get_contents() ( dokumentacija ) pa funkcijom json_decode() pretvoriti dobiveni sadržaj u obliku JSON u polje s podacima o osobama. Ako datoteka ne postoji, popis osoba mogli bismo inicijalizirati na prazno polje. U tom slučaju bi prva osoba koju kasnije dodamo bila postavljena u polje kao element na indeksu 0 i imala isti taj broj kao identifikator za dohvaćanje putem URL-a. Kako ljudi intuitivno preferiraju brojati od 1, dodat ćemo u to polje proizvoljni element da zauzme indeks 0 pa će prva kasnije dodana osoba biti postavljena na indeks 1. Kako element na indeksu 0 nećemo koristiti, njegov nam sadržaj nije bitan pa možemo iskoristiti vrijednost NULL . Note Uočimo da prazno polje, vrijednost NULL i polje koje sadrži vrijednost NULL nisu iste vrijednosti: prvo je polje bez elemenata, drugo nije polje, a treće je polje s jednim elementom; tip tog elementa ne mijenja činjenicu da polje ima element. Kod u datoteci index.php je oblika: count ( $persons ), "results" => array_values ( $persons )]; $response_body = json_encode ( $response_body_array ); echo $response_body ; } else { http_response_code ( 400 ); } Podsjetimo se da je HTTP metoda GET zadana pa isprobajmo dohvaćanje URI-ja /persons na način: $ curl -v localhost:8000/persons * Trying ::1:8000... * Connected to localhost ( ::1 ) port 8000 ( #0) > GET /persons HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse GET /scientists HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse count ( $persons ), "results" => array_values ( $persons )]; $response_body = json_encode ( $response_body_array ); echo $response_body ; } else if ( $_SERVER [ "REQUEST_METHOD" ] == "GET" && preg_match ( "/^\/persons\/[1-9][0-9]*$/" , $_SERVER [ "REQUEST_URI" ])) { $uri_parts = explode ( "/" , $_SERVER [ "REQUEST_URI" ]); $id = $uri_parts [ 2 ]; if ( array_key_exists ( $id , $persons )) { $response_body_object = $persons [ $id ]; $response_body = json_encode ( $response_body_object ); echo $response_body ; } else { http_response_code ( 404 ); } else { http_response_code ( 400 ); } Isprobajmo dohvaćanje nekoliko URI-ja: $ curl -v http://localhost:8000/persons/1 * Trying ::1:8000... * Connected to localhost ( ::1 ) port 8000 ( #0) > GET /persons/1 HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse GET /persons/599 HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse GET /persons/007 HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse POST / HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.72.0 > Accept: */* > Content-Type: text/plain > Content-Length: 9 > * upload completely sent off: 9 out of 9 bytes * Mark bundle as not supporting multiuse $person [ "name" ], "birth_year" => $person [ "birth_year" ], "known_for" => $person [ "known_for" ]]; http_response_code ( 201 ); } else { http_response_code ( 400 ); } $j = json_encode ( $persons ); file_put_contents ( $datoteka , $j ); Naposlijetku nam ostaje dodati još datum i vrijeme stvaranja i uređivanja ( "created" i "edited" ) te URL zapisa ( "url" ). Datum i vrijeme stvaranja i uređivanja će ovdje biti isto jer tek stvaramo osobu, a oblik u kojem su ta vremena zapisana u našim podacima je ISO 8601 . U jeziku PHP je moguće dohvatiti trenutno vrijeme i datum u nekom obliku funkcijom date() ( dokumentacija ), a popis podržanih oblika uključuje DATE_ISO8601 koji nam odgovara. Kako bismo dobili URL, iskoristit ćemo funkciju array_key_last() ( dokumentacija ) da saznamo identifikator posljednjeg elementa polja, a to je onaj upravo stvoren. Zatim ćemo operatorom za spajanje znakovnih nizova ( . ) spojiti zadani početak URL-a http://localhost:8000/persons/ s dobivenim identifikatorom. PHP će pritom za nas izvesti pretvorbu identifikatora iz cjelobrojnog tipa u znakovni niz pa o tome ne moramo brinuti. $person [ "name" ], "birth_year" => $person [ "birth_year" ], "known_for" => $person [ "known_for" ], "created" => $date_time , "edited" => $date_time ]; $id = array_key_last ( $persons ); $persons [ $id ][ "url" ] = "http://localhost:8000/persons/" . $id ; http_response_code ( 201 ); } else { http_response_code ( 400 ); } $j = json_encode ( $persons ); file_put_contents ( $datoteka , $j ); Isprobajmo stvaranje osobe na način koji smo upravo razvili. Za početak se uvjerimo da zahtjevi čiji sadržaj ...