Sådan gør du: 3 gode råd til at kode webtjenester med REST

Den simple designmodel til webtjenester, REST, har de senere år gjort stadig større indhug i markedsandelene fra konkurrenterne SOAP og WSDL.
Men det er stadig langt fra alle webprogrammører, der fuldt ud retter sig efter REST's retningslinjer. Det mener udvikler hos det danske it-konsulenthus Jayway, Mads Enevoldsen.
»REST bliver anvendt rigtigt mange steder i dag, men der sker ofte misforståelser omkring, hvad REST egentlig er,« siger Mads Enevoldsen til Version2.
REST står for REpresentational State Transfer og er en arkitektur, der kort fortalt ridser op, hvordan man bruger grundlæggende webteknologier som HTTP og URI'er.
Følger man REST-principperne, ender man op med webapplikationer og -tjenester, som udnytter nettets grundlæggende arkitektur bedst muligt, lyder tankegangen bag.
Ifølge Mads Enevoldsen er et af problemerne, at webudviklerne ofte mangler de rette værktøjer, der gør dem i stand til at programmere efter REST-principperne.
Af samme grund har han udviklet et open source REST-framework med navnet Forest, som benyttes internt hos Jayway, men også har været brugt på en konkret opgave hos en kunde, den svenske onlineboghandel dito.se.
Forest er tænkt som en hjælpende hånd til dem, der gerne vil eksponere et REST-API til deres webservices.
Mads Enevoldsen er hentet ind som oplægsholder på den verdenskendte Java-konference JavaOne i oktober, hvor han skal fortælle om sine erfaringer med best practices inden for REST.
Her får du et par af hans gode råd omkring, hvordan man følger REST bedst muligt:
1. Gør din klient agnostisk
En REST-klient skal have generisk protokolviden omkring hvordan serverinteraktion sker - og ikke andet. Klienten må altså ikke gøre sig antagelser omkring URL-hierarkiet eller selv foretage URL-konstruktion - dette foretages kun af serveren. Tænk på REST-klienten som dig selv foran en browser: når man navigerer et website, følger man links, som præsenteres for en; man skriver ikke selv i browserens adresse-baren.
2. Brug HTTP til fulde
Et eksempel på misbrug af HTTP er, hvis man indbygger mediatype-informationen i URL'en. Det kan for eksempel være, at /myservice/resource returnerer ressourcen i HTML, og /myservice/resource.json returnerer ressourcen i JSON. HTTP har en header som anvendes til dette. Tilsvarende findes statuskoder, som nuancerer kommunikationen mellem klient og server. Her kan fordelene blandt andet være, at man 'gratis' mindsker load'et på sin service ved at benytte webbets cachinginfrastruktur.
3. Vær hypermedia-dreven
En definerende egenskab ved REST er HATEOAS (Hypermedia As The Engine Of Application State). Et REST-API tilgås uden anden antagelse end root-URL'en og en generisk forståelse af HTTP og standard mediatyper. Herefter er det serveren, som fortæller klienten (via hypermedia content), hvilke muligheder den har. Klienten driver så tilstanden ved at eksercere disse hypermedialinks.
Forest (Mads Enevoldsens eget REST-API, red.) benytter reflection ud fra ressourse-træet og konstruerer en tilsvarende URL-struktur. Dermed slipper man få at selv skulle håndtere en masse low-level protokoldetaljer direkte i koden. Forest giver dig en HATEOAS REST-service ud fra et POJO-ressourse træ.
Artiklen er skrevet som led i Version2's Sommertour 2012, hvor redaktionen sommeren igennem besøger it-virksomheder og rapporterer om medarbejdernes dagligdag og virksomhedens projekter. Se den samlede tour-plan her, hvor du også har mulighed for at stille spørgsmål til de besøgte virksomheder.
Kommentarer (6)
Jeg kender godt de ovenstående principper, men har aldrig helt set det store lys i at det skal foregå på den måde. Jeg vil meget gerne uddannes. Jeg mener hvis man har en central API dokumentation er det så ikke fint?
Hvad er det fantastiske muligheder der åbner sig når man bruger HATEOAS?
De klienter jeg har været med til at skrive indeholder alligevel specifik kode til hvert endpoint, og har mange antagelser om hvordan API'et fungerer. Hvordan fjernere man nogle af disse antagelser i praksis og hvilke fordele giver det?
Et praktisk eksempel der har demonstreret fordelene ville være godt (ikke bare teori).
REST som design filosofi er fantastisk i forhold til alternativerne, og dejlig pragmatisk og fleksibelt.
Jeg mener ligesom Allan heller ikke at reflection (auto-discovery and connectedness) er essentielt, med mindre vi snakker en meget speciel kategori af aplikationer man ønsker at kunne autogenerere proxier til - hvor WADL ikke er nok (men er dette ikke mest af alt et teoretisk hypermedie-ønske?!).
Derudover, er jeg også lidt uenig med at det skulle være misbrug af HTTP, at forhandle repræsentation via URI'en. Det er rigtigt at man kan udnytte HTTP til dette, men man mister addressability (også kaldet statelessness) når man ikke kan sende en URI til en kollega der indeholder den komplette information omkring sådanne ting som sprog (.../da/... frem for Content-Language: da), (".../pdf" frem for "Content-Type: application/pdf") osv. Jeg plejer at skrive mine API'er således at der faldes tilbage på HTTP headers, men hvor så meget som muligt (dvs. alt undtagen auth) er udtrykt via URI'en.
Det svære ved REST, er uden tvivl, at designe URI'er over ens forretnings-domæne så de skalerer og versionerer.
I praksis ryger man dog hurtigt ind i hvad jeg kalder "projektions problemet", hvor man har brug for at se samme resource på en lidt anden måde... med JAX-RS/Jersey og JAXB POJO mapping kommer man hurtigt til kort, fordi man i så fald skal lave forskellige POJO'er der repræsenerer desse views - ofte med nestede hierakier - og så ruller lavinen med en eksplosion af POJO'er som resultat. I teorien skal man i REST eksponere hver resouce for sig, og linke rundt. I praksis bliver dette dog ofte til alt for man små kald til server og (især) database.
Derfor er vi hvor jeg arbejder, ved at implementere OData overbygning, hvor URI'en bliver et minimalistisk query sprog, hvor projektionen beskrives og hierakier kan flettes sammen efter behov. Dette ser jeg som den ultimative mashup, og med stort fremtidig potentiale. F.eks. kan vores GWT/Dart/JavaScript klienter helt undvære en dedikeret backend, da frontend-udviklerne blot beskriver projektionen i deres query. Ja faktisk ser vi også fordele i B2B scenario, da vi ikke behøver at give direkte database adgang og kommer ud over dén irreterende JDBC begrænsning hvor kun tabulære data kan overføres (mange små queries uden redundante data eller få store med redundante data).
Del 2 burde være oplagt: GET kan caches og det kan POST ikke. PUT og DELETE er idempotente, GET er nulpotent osv. Ved at benytte HTTP statuskoder, så skræller du et lag af i din klient til at håndtere fejl. Nu skal du kun bekymre dig om 2 klasser (den underliggende forbindelse og statuskoderne) og du slipper for at skulle opfinde dit eget format for fejlbeskrivelser.
Specielt cacheregler bliver også en del nemmere at håndtere med del 2.
Del 3 og 1 handler om at du ikke har kontrol over din klient. Hvis du nu koder op mod Apples App Store som distributionsmedie, så tager det tid at få godkendt en ændret app. Dertil kommer folk som ikke gider at opgradere deres program. Problemet er mindre med webklienter der downloades hver gang. Ideen er at du på serversiden kan styre cachepolitik, hvor mange requests der laves for at hive hele siden og så videre. Hvis f.eks. du har en del data som meget sjældent ændrer sig, så er det en fordel at have dem i en separat cache-able request. Ved at gøre kommunikationen til et program der fortolker det modtagne og styrer requests har du fået mere frihed uden at du skal ind og pille i gammel software.
Hvad dit endpoint gør i et givent tilfælde er naturligvis applikationsspecifikt. Tricket her handler mere om at gøre kommunikationen mellem 2 peers smartere, så der er mindre statisk kode.
Om det så er smart... Tjah. Jeg tror ikke HTTP er godt nok til en del ting og det er også forbandet latterligt at vi kun for nylig har fået mulighed for at lave websockets. REST virker lidt som et kludge i nogen tilfælde.
Ja det er HATEOAS delen jeg ikke har set lyset i, det andet praktiserer jeg selv.
Hvad dit endpoint gør i et givent tilfælde er naturligvis applikationsspecifikt. Tricket her handler mere om at gøre kommunikationen mellem 2 peers smartere, så der er mindre statisk kode.
Jeg har bare meget svært ved at forestille mig hvordan jeg kan gøre det i praksis med HATEOAS, i de applikationer jeg sidder med. Det virker lettere fortænkt og jeg har ikke læst en eneste artikel om et projekt der har brugt det med success, hvor det var en udslagsgivende faktor. Tværtimod virker det som en masse ekstra arbejde for ingen gevinst. Og når det, som jeg forstår det, bliver fremført som den eneste rigtige måde at lave REST i artiklen, bliver jeg mildest talt skeptisk.
Kan du give et lille praktisk eksempel på hvordan du kan undgå at opdatere din app i Apple store?

