RESTful on määrite, joukko vaatimuksia, jotka kaikki pitää täyttää, jotta rajapinta olisi todella REST -rajapinta. Osa asioista, joita REST -rajapintaan liitetään, on kiistanalasia, osa taas paremmin hyväksyttyjä. Yksi ohje, johon REST -rajapintoja usein peilataan, on Leonard Richardsonin kirjoittama Richardson Maturity Model.
Richardson määrittelee rajapinnan maturiteettiasteen neliportaiseksi.
-
Ensimmäisellä portaalla on "The Swamp of POX". Käytännössä tässä tehdään RPC (Remote Procedure Call) -kutsuja HTTP:n yli, käyttäen koko HTTP:ta vain tunnelina.
-
Toisella portaalla mukaan tulevat Resurssit. Sen sijaan, että kutsuttaisiin joka kerta jotain tiettyä yleis-yhteyspistettä, käytetään useita yhteyspisteitä, jotka kukin kuvaavat jotain resurssia. Usein jo tätä tasoa aletaan (kyseenalaisesti) kutsumaan RESTiksi.
-
Kolmannella tasolla mukaan otetaan HTTP Verbit. Tätä tasoa jo huomattavan moni kutsuu RESTiksi. Käytännössä hyödynnämme tilanteeseen sopivaa HTTP verbiä (GET, POST, PUT, DELETE, ...) sen mukaan, mitä haluamme kyseisen yhteyspisteen takana olevalle resurssille tehdä. Eli GET /places hakee resurssilistauksen, POST /places tallentaa uuden resurssin, DELETE /places/1 poistaa tietyn resurssin ja niin edelleen.
-
Neljännellä kypsyystasolla mukaan tulee Hypermedia Kontrollit ja akronyymi HATEOAS (Hypermedia as the Engine of Application State). Tällä tasolla, Richardsonin määritelmän mukaan, olemme 100% RESTful -tilassa.
Edellisessä kirjoituksessa lähdimme liikkeelle parantelemaan huonosti tehtyä tason 3 rajapintaa, joka teknisesti ei ollut REST -rajapinta. Koska tässä pelissä se on kaikki tai ei mitään, ei voi olla mitään miltei REST -rajapintaa. Internet on täynnä vihaisia miehiä ja naisia valvomassa termien puhtautta.
Käydään siis kursorisesti läpi, mitä meidän tulisi vielä muuttaa, jotta olisimme todella RESTful.
HATEOAS
Vaikeampi lausua, kuin toteuttaa. HATEOAS tarkoittaa oikeastaan vain kahta asiaa:
- Content negotiation (eli lisätään Accept -otsake)
- Hypermedia kontrollit
Content negotiation
Jotkut itsestään RESTful -nimeä käyttävät rajapinnat (i.e. Twitter) toteuttavat content negotiationin käyttämällä tiedostopäätteitä. Tällöin yhteyspiste saattaisi olla seuraava:
/statuses/show.json?id=xxx
/statuses/show.xml?id=xxx
Tässä kuitenkin rikotaan REST -rajapintojen henkeä sijoittamalla yhteyspisteeseen sine kuulumatonta informaatiota. Käyttäjän tulee tietää, että show -yhteyspiste on olemassa varsinaisen resurssin päällä JA hänen pitää tietää tiedostopääte, sekä id -parametri. Enemmän RESTiä olisi ollut yhteyspiste /statuses/xxx.
Tällöin rajapinta vastaa joko oletustiedostorakenteellaan tai kunnioittaa Accept -otsaketta ja joko palauttaa tiedon pyydetyssä rakenteessa tai 415 -tilakoodin, jos pyydettyä rakennemuotoa ei tueta. Mitään id -parametria ei enää tarvita.
Rajapinnan tulee olla joukko yhteyspisteitä, ei kokoelmaa JSON- tai XML-tiedostoja. Palautettu rakennemuoto tulee Accept -otsakkesta, eikä kuulu yhteyspisteen nimeen. Sama pyyntö olisi voinut näyttää tältä.
GET /statuses/xxx HTTP/1.1
Host: api.twitter.com
Accept: application/json
Nykyään aika moni rajapinta palauttaa oletusrakenteenaan JSON:ia. Mielestäni se on hyvä. XML on ilmaisuvoimaisempi joissain tapauksissa vahvan rakenteisuutensa vuoksi, mutta myös raskaampi tulkita ja vaatii skeeman. Jos voimme hyväksyä sen, että useissa rajapinnoissa pelkkä JSON -rakenne riittää, meidän ei tarvitse huolehtia content negotiationista niin paljoa. Riittää, että vastaamme sopivalla tilakoodilla, jos pyydettyä muotoa ei ole tarjolla.
Hypermedia kontrollit
Se toinen osa HATEOASista, eli se viimainen askel kohti todellista RESTful -rajapintaa. Tätä osaa ei valitettavasti kovin usein näe toteutettuna. Usein sanotaan mitä merkillisimmistä pikku detaljeista, että tuo ei ole RESTful. Mutta tässä kohdassa, melko vahvan konsensuksen mukaan, ilman tätä se todella ei ole RESTful.
Phil Sturgeon esitti edellisessä kirjoituksessa mainitussa kirjassaan, että jonkin arvion mukaan 74% sellaisista rajapinnoista, jotka kuvaavat itseään REST -rajapinnoiksi, ei toteuta hypermedia kontrolleja. Richardsonin mallin mukaan tasot 1, 2 ja 3 eivät ole "REST" ja vasta taso 4 on "REST".
Hypermedia kontrollit tulevat HTML -maailmasta. Meillä on linkkejä, jotka ohjaavat muihin kutsuihin. Eihän tämä ollutkaan kovin vaikeaa. Linkit voivat ohjata uuteen sisältöön, relaatioihin, tai toimintoihin. Näin rajapinnan kuluttaja voi liikkua rajapinnassa, kuin verkkosivustolla. Kantavana ajatuksena on se, että rajapinta on sekä koneen, että ihmisen tulkittavissa ilman dokumentaatiota.
Jos palautamme yhteyspisteestä yhden resurssin, se on täysin irrallinen osa muusta rajapinnasta. Siitä puuttuu relaatio muuhun rajapintaan. Rajapinnan kuluttajan täytyy siis katsoa dokumentaatiosta, mitä muita yhteyspisteitä voitaisiin käyttää.
{
"data": [
"id" 1,
"name": "Testipaikka",
"address1": "Testikatu 1",
"zip": 00459,
"links": [
{
"rel": "self",
"uri": "/places/1"
},
{
"rel": "place.image",
"uri": "/places/1/image"
}
]
]
}
Lisäsimme siis resurssiin pari linkkiä ja niiden relaation melko ihmis-luettavassa muodossa. Kun saame tämän resurssin, tiedämme suoraan, mistä löydämme siihen liittyvän kuvan (aliresurssin). Tiedämme lisäksi, mikä on tämän resurssin sijainti ("rel": "self"). Mutta miksi me sitä tarvitsisimme?
Jos haemme resurssin GET /places/1, tiedämme jo resurssin osoitteen. Mutta jos olemme vaikka lisäämässä uutta resurssia POST /places, meillä ei ole suoraan tietoa uudesta osoitteesta. Voisimme sen parsia kasaan uuden id:n mukaan, mutta voimme myös antaa sen suoraan.
Ok, mutta eikö noihin linkkeihin pitäisi liittää HTTP verbit mukaan? Mistä rajapinnan käyttäjä tietää, mitä toimintoja yhteyspiste tukee? Tässä kohdassa apuun tulee aivan tietty HTTP verbi: OPTIONS.
OPTIONS /statuses/xxx HTTP/1.1
Host: api.twitter.com
Tämä pyyntö saattaisi vastata esimerkiksi vaikka seuraavasti:
HTTP/1.1 200 OK
Host: api.twitter.com
Connection: close
Allow: GET,HEAD,POST
Eli voimme kysyä rajapinnalta, mitä toimintoja saamme tehdä kyseisellä yhteyspisteellä. Ja rajapinta vastaa. Rajapinta siis dokumentoi itse itseään. Ja on lisäksi melko helposti ihan ihmisenkin luettavissa.
Okei, tähän kohtaan on sanottava, että useat isot ohjelmistotalot eivät salli OPTIONS -kutsua muka-REST -rajapintaansa. Se ei silti tarkoita sitä, etteikö sellainen kuuluisi sallia.
Lopuksi
Tämä kirjoitus pyrki toteuttamaan REST -rajapintaa Richardson Maturity Modelin mukaan. Esitetyt asiat ovat varmasti kiistanalaisia. Koska Googlekaan ei tee REST -rajapintojaan RESTeiksi, niin miksi minun pitäisi? Tai koska GitHub ei tee? Itse en useinkaan noudata kaikkea pilkulleen, mutta on mielestäni hyvä tuntea perusteet ennen kuin käy niitä rikkomaan.
Tästä huolimatta HATEOAS tuo selkeän hyödyn rajapintaan. Palautettavan datan rakenteen valinnan poistaminen yhteyspisteen polusta siistii yhteyspisteen polun vakiinnutettavaksi. Poistetaan polusta kohdat, jotka eivät sinne kuulu. Pieni asia, mutta arkkitehtuurin kannalta iso merkitys. Toinen asia, linkkien mukana kuljettaminen puolestaan helpottaa merkittävästi rajapintaa hyödyntävän tahon työtä. Niiden lisääminen ei ole iso työ. Niiden puuttuminen voi johtaa isoon työhön.
Jälleen kerran isona lähteenä toimi Phil Sturgeon kirja Build APIs You Won't Hate.