✉️ Saņem šito visu e-pastā. Tā vietā, lai palaistu garām kaut ko no tā, ko es rakstu savā blogā, tagad vari pierakstīties un saņemt e-pastā visu, ko es te rakstu. Tas nav bieži.

← Uz sākumu

REST-ish

24. februārī, 14 komentāri

Šis laikam ir no tās sērijas, kad ar aizdusu raksti, raksti, jo galvā ir baisi forša un lieliska doma, bet tad saproti, ka uz virtuālā papīra nemaz tik naski neliekas. Parasti tas paliek melnrakstos. Šoreiz nolēmu nedaudz piepucēt un saglabāt publiskā vietā.

2000. gadā kāds Roy Thomas Fielding publicēja savu doktora disertāciju "Architectural Styles and the Design of Network-based Software Architectures". Pati disertācija ir par vienotu veidu kā izprast un izvērtēt dažādu datortīkla aplikāciju arhitektūras. Kā case study Fīldings nolēma paņemt WWW, jeb REST. Un tā apraksts aizņem tikai vienu nodaļu no sešām.

Patiesais stāsts ar REST piemēru bija tas, ka tādējādi Fīldings nodemonstrēja vispārējo tā brīža WWW arhitektūru, hiperteksta nozīmi, kā arī tā panākumu atslēgu. Viņš neizgudroja REST.

Tas varbūt ir diezgan interesanti - lasīt par agrīno webu (HTTP + HTML) tik akadēmiskā formātā. Bet tanī pat laikā, tas mirklis, kad saproti, ka REST ar mūsu API nav absolūti nekāda sakara, bija maķenīt patīkami atsvaidzinošs.

Tur minētās tēzes diezgan bieži ieklīst manās pārdomās par dažādu sistēmu vai risinājumu dizainiem. Jo labi saprotamai sistēmai pamatā ir vienkārša arhitektūra. Vai arī, ja tā ir sarežģīta, tad saskarnes ir maksimāli triviālas un pašsaprotamas.

Fīldings savā disertācijā aprakstīja sešus pamatprincipus, kuri tika iestrādāti pašā HTTP kā transportā, HTML kā medijā un hipersaitēs kā norādēs. To visu lasot ieteicams ir padomāt par to, cik ļoti modernie steki to visu nereti vēlas pārkāpt.

  1. Klienta-servera arhitektūra ar mērķi nodalīt klientu no servera tiktāl, ka katrs no tiem var attīstīties neatkarīgi. Serveris nodrošina datu glabāšanu un biznesa loģiku, bet klients - saskarni un lietotāja stāvokli.
  2. Stāvokļneatkarība? Angliski - statelessness. Katrs pieprasījums uz serveri un atbilde no tā satur visu nepieciešamo informāciju, lai klients spēja savā pusē uzturēt nepieciešamo sesijas stāvokli. Serveris sesijas stāvokli neglabā. Piemēram, sesiju kūkiji šo principu salauž. Interesanti, ka JWT to nesalauž, jo sesijas stāvoklis ir pie klienta un pieprasījums ietver visu nepieciešamo informāciju, lai serveris varētu sniegt informētu atbildi.
  3. Katram resursam ir jādefinē vai tas ir kešojams un kā, kā var tikt pie tā svaigākās versijas, utt. Tas ir "iecepts" HTTP protokola keša kontroles hederos.
  4. Viendabīga saskarne. Saskarnei ir jābūt tādai, lai tai varētu piemērot visvienkāršāko iespējamo arhitektūru. Šis laikam ir vissvarīgākais.
    • Resursiem jābūt viennozīmīgi identificējamiem (piemēram, dokumentam vienmēr būs adrese /document/123).
    • Darbības un manipulācijas ar resursiem notiek tikai caur to reprezentāciju. Tas nozīmē, ka klients, saņemot to pašu dokumentu, saņem arī informāciju kādas darbības ar to ir pieejamas. Nekas netiek pieņemts par pašsaprotamu. HTML gadījumā tās ir formas.
    • Jebkuras manipulācijas notiek, pārsūtot jauno reprezentāciju. Klients pieprasa esošo, izmaina, nosūta serverim. Un nekā citādāk. Dzēšanai tas arī strādā, jo jaunā reprezentācija ir 'neeksistējošs', jeb null.
    • Runājot par iespējamajām manipulācijām. API gadījumā tas reti tiek izmantots, jo klientam ir jāzin - kādas HTTP metodes kad ir jāizmanto. Bet REST nosaka, ka šai informācijai ir jānāk no servera. Teorētiski tam derētu OPTIONS, bet paceļ roku - kurš to nodrošina visiem endpointiem? :D Un savā ziņā tas ir out-of-band - kad jāveic papildu manipulācijas, lai noskaidrotu to, kas jāuzzin no resursa paša.
    • Tas, ko saņem klients, ir resursa reprezentācija, nevis pats resurss. Klients var palūgt un serveris var spēt piegādāt to pašu resursu citā formātā (JSON dokuments, XML dokuments, bildīte, Word fails, utt)
    • Reprezentācijai ir jāsatur gan paša resursa dati, gan tā metadati.
    • Katram ziņojumam ir jāsatur visa nepieciešamā informācija, lai to varētu saprast. Atkal - nekas nav pašsaprotami. Šis ir HTTP protokola pamatā. Metodes (GET, POST, PUT, utt), vienošanās par satura veidu (Content-Type, Accept hederi). Respektīvi - visam kontekstam, kurš nepieciešams, lai ziņojumu saprastu, ir jābūt pašā ziņojumā.
    • Un pēdējā laika baisākais akronīms - HATEOAS (Hypermedia as the Engine of Application State). Serveris piedāvā klientam saites, kuras norāda to, ko ar to resursu var vispār izdarīt.
  5. Slāņi. Klientam nav obligati jāspēj pateikt vai tas saņem informāciju no starpniekservera vai pa tiešo no oriģinālservera. Lai tur pa ceļam ir miljons API vārteju, reverso proxy serveru, CDNu, slodzes dalītāju un WAFu. Klientam vienalga.
  6. Un vienīgā neobligātā prasība - serveris var piegādāt arī kodu, kuru klients var palaist, lai viss būtu frošāk. Tā varētu būt Java apleta formā deviņdesmitajos vai JavaScript formā šodien.

Lai aplikācija būtu atbilstoša REST, tai jāatbilst pieciem pirmajiem punktiem. Sestais ir neobligāts.

Tāpēc mēs tos API arī saucam par RESTful :) Tie atbilst pamatprincipiem, bet vairums no populārākajām specifikācijām atkāpjas no viena vai vairākiem punktiem.

JSON:API, piemēram, ir diezgan strikts piekritējs visam, ko Fīldings aprakstīja. Ar mazu atšķirību tanī, ka dod iespēju veikt masveida darbības caur vienu endpointu.

JSONLD neseko īsti HATEOAS principam, jo lai arī saites dokumentos eksistē un ir pats pamatu pamats šai specifikācijai, tās neapraksta darbības. Tas vairāk fokusējas uz reprezentācijas pusi, ne manipulācijām.

Mūsdienās mēs uz REST skatāmies kā uz pamatu API būvēšanai, bet ironiskā kārtā REST API biežāk nekā retāk nemaz neseko šiem principiem. Mūsdienās tas vairāk ir HTTP RPC, kur klientam ir jāzin - ko izsaukt, kā izsaukt un kā vispār kaut ko atrast.

Tu atbildi augstāk redzamajam komentāram. Atcelt

Gravatar Vilx-

24. februārī, plkst. 02:56

Manī jau ļoti sen izraisa diskomfortu cilvēku milzīgā spiešana uz to "REST"-iskumu API dizainā. Daudzas no šīm izvēlēm šķiet... neracionālas un neoptimālas. Piemēram, tas pats HATEOAS API kontekstā ir vienkārši... WTF???? Un nesen es uzdūros rakstam, kas beidzot salika punktus uz "i" un es sapratu, no kurienes ceļas šī kongnitīvā disonanse. Atstāšu linku: https://unixdigest.com/articles/no-your-api-isnt-rest-but-dont-worry-it-really-shouldnt-be.html

Gravatar Vilx-

24. februārī, plkst. 03:03

Ja slinkums lasīt to rakstu (nav garš), tad to perfekti sumarizē teikums šī raksta vidū:

REST applications are for people, not machines!

Gravatar Kaspars Autors

24. februārī, plkst. 07:54

Gluži manas domas.

Gravatar Kaspars Autors

24. februārī, plkst. 08:04

PS. Bet jāsaprot, ka ne visi API ir machine-to-machine.

Gravatar Vilx-

24. februārī, plkst. 10:44

Interesanti... šobrīd nevaru iedomāties pretpiemēru. Ja tas nav taisīts mašīnai, tad to parasti nesauc par API un otrādi. Vari pateikt kādu piemēru, kas būtu "API", bet nebūtu domāts mašīnai?

Gravatar Kaspars Autors

24. februārī, plkst. 12:21

Vairums API, kuri ir domāti frontendam. Tie, kas ir domāti serviss-servisam, nereti ir ar citiem dizaina konstreintiem.

Gravatar Vilx-

24. februārī, plkst. 12:59

OK, tad mēs kaut ko atšķirīgu domājam ar "machine". Es ar to saprotu interfeisu, kura rezultātu apstrādā programma. Frontends tai skaitā. Jo gala lietotājs (cilvēks) jau par šiem API izsaukumiem neko nezina - viņš tikai strādā ar savu UI, kurš tos veic fonā pēc vajadzības, un prezentē lietotājam jau kaut kā apstrādātu rezultātu.

Gravatar Kaspars Autors

24. februārī, plkst. 13:30

Nu, pa manam ir vienkārši. Klienta-servera aplikācijas gadījumā ir vēlams ievērot REST arhitektūru. Gan discoverability, gan viss pārējais. Es, godīgi sakot, nevaru iedomāties nevienu keisu, kur tas būtu pretrunā ar klienta-servera UI konceptu. Protams, eksistē arī citi arhitektūras veidi, bet tas ir tikai normāli.

Gravatar ulzha

24. februārī, plkst. 20:41

JWT jau arī validēt vajag. Ja mēs gribam drošību, autorizāciju, kas ir atsaucama, tad man nav skaidrs vai tas ir savietojams ar 100% "stateless" kā tajā nodaļā rakstīts.

Gravatar Kaspars Autors

24. februārī, plkst. 21:05

Es teiktu, ka JWT tokenu atsaukšanas scenārijs visu padara non-REST. Bet, godīgi sakot, es vēl neesmu saskāries ar tādu nefunkcionālo prasību. Līdz ar revokāciju nāksies atvērt palielu tārpu bundžu.

Bet, ja mēs nerimplementējam revokāciju, tad validācija (paraksta verifikācija), savukārt, nekādi nekonfliktē ar REST pamatprincipiem.

Gravatar Vilx-

24. februārī, plkst. 22:24

Kā es esmu novērojis, tad šobrīdējā parastā prakse ir tāda, ka pie ielogošanās auth serveris aplikācijai izsniedz 2 JWT tokenus - "lietošanas" un "refresh". Lietošanas tokens ir ar īsu expiry time - pēc vēlēšanās tas var būt no dažām dienām līdz pat dažām minūtēm, bet samērā īss. Refresh tokens savukārt ir ar garu expiry, un to tajā pašā auth serverī var iemanīt pret svaigu lietošanas (un nākamo refresh) tokenu, kad vecais lietošanas tokens expirējas. Rezultātā, "revokācija" kā tāda neeksistē, bet noplūduši tokeni ir derīgi tikai ļoti īsu brīdi. Un izdotos refresh tokenus, ja vajag, var revokēt tajā pašā auth serverī. Vai tas salauž REST - nezinu. Ja pašus tokenus uztver kā resursus - moš arī nē.

Gravatar Kaspars Autors

24. februārī, plkst. 22:41

Ja runājam par sfēriskajiem zirgiem vakuumā, tad jā - tokenu pāris risina access tokena nozagšanas problēmu. Tas nekādi nerisina refresh tokena pazaudēšanu, jo tiem parasti ir gana ilgs derīguma termiņš, lai varētu izdarīt visus netīros darbiņus un vēl pāri paliks.

Bet lai arī pašam nav nācies tādu sistēmu veidot, esmu drošs, ka var uzzīmēties vajadzība garantēti ieviest revokāciju. Iespējams, ka compliance (vai iedomu compliance) dēļ. Vai arī tamdēļ, ka atnāk kāds un pasaka, ka drošībai jābūt drošai un tagad būs tā.

Savukārt, ja par REST, tad autentifikācija ir fine - visi pieprasījumi satur visu nepieciešamo informāciju. Bet revokācija ir nedaudz tomēr ārpus šī visa, jo prasa serverim veikt out-of-bounds pieprasījumu, lai nočekotu tokena validitāti - tādējādi pieprasījums vairs nesatur visu nepieciešamo informāciju, lai to izpildītu.

Iespējams, ka ne velti Fīldings neko diži daudz nerunā par auth, ja neskaita kūkiju roastošanu. Man šķiet, ka vēlākos gados viņš pats kaut kur minēja par to, ka tokenu autentifikācija atbilst REST pamatprincipiem. Noteikti lasīju šī ieraksta researcha laikā, bet vairs neatrodu.

Gravatar Vilx-

25. februārī, plkst. 12:44

Te jau viss sāk palikt tik slidens... ja neredzams backend pieprasījums uz ārēju resursu salauž REST, tad kas ir ar DB? Vai tas arī nav ārējs resurss? Kā ar failsistēmu? Un ja nu kaut kāds izsaukums uz ārēju resursu ir nepieciešams pieprasījuma apstrādē pēc būtības? Piemēram, Tu onlainā pērc pasākuma biļeti, un Tev jāaizsūta pirkuma info uz pasākuma rīkotāja IT sistēmu? Utml.

Tāpēc es pats jau sen esmu atmetis domāšanu par Svēto RESTu, un vienkārši taisu tā, kā izskatās vieglāk. 😅

Gravatar Kaspars Autors

25. februārī, plkst. 13:41

REST jau nav vienīgā pareizā arhitektūra. Negribi - neievēro. Gribi - ievēro daļēji. Gribi uz 100%, ej uz 100%. Tas ir lēmums, kuru neviens cits nepieņems tavā vietā.