O REST na Zdrojáku již jeden článek vyšel, takže je-li pro vás tento pojem neznámý, určitě si přečtěte článek od M. Malého REST: Architektura pro webové API.
Ačkoliv REST není přímo s Node.js nijak spjat, myslím si, že je dobré věnovat mu trochu více prostoru, aby bylo v pozdějších dílech seriálu čtenáři hned jasné, proč je třeba vrácen právě daný HTTP kód a ne jiný. Kromě toho je REST API jedna z oblastí, pro kterou se Node.js používá velmi často.
V tomto článku se nebudu věnovat autorizaci a autentizaci ani kešování obsahu. Obě témata si zaslouží samostatný díl a budeme se jim věnovat v pozdějších dílech seriálu.
Proč REST API
Pokud budu uvažovat náš vyvíjený e-shop, může se REST API využít při mnoha příležitostech:
- Webové rozhraní pro nákup v e-shopu. V našem případě řešeno přes HTML5 a framework AngularJS.
- Administrační rozhraní. Velmi často provozuje jeden zákazník více e-shopů, takže může požadovat jedno administrační rozhraní pro všechny e-shopy.
- Import dat. Téměř všechny e-shopy importují produkty z nějakých externích systémů. Buď se tento systém sám na náš e-shop napojí, nebo vytvoříme „můstek“, který data z daného systému překlopí přes API do našeho systému.
- Export dat. Každý e-shop potřebuje data někam exportovat, např. do účetního systému.
- Různé další projekty, které potřebují s e-shopem komunikovat. Žijeme v době startupů, kdy i nejjednodušší problém řeší mnoho zajímavých projektů. Ty však potřebují nějak automatizovaně s naším e-shopem komunikovat.
- Verze pro smartphony a tablety. Můžete chtít speciální mobilní verzi pro Android či iPhone, která ale potřebuje odněkud získávat data. Máme-li např. e-shop pro prodej jízdenek, budeme určitě chtít i mobilní verzi.
- Mnoho dalších možností, affiliate systémy nebo třeba reklamní aplikace s katalogem mobilních telefonů. A data do aplikace budeme importovat z našeho e-shopu s telefony (samozřejmě v detailu telefonu nezapomeneme na tlačítko „Koupit“).
Nejlepší postupy pro REST
Dále popisuji základní doporučení návrhu relevantní k vyvíjeném e-shopu. Máte-li zájem o hlubší prozkoumání problematiky, pak lze doporučit knihu REST API Design Rulebook nebo REST tutoriál.
Bohužel každá kniha věnující se REST se v různých oblastech do značné míry rozchází. Podobně je tomu v případě API známých serverů typu Facebook či Twitter. Následující řádky obsahují mix doporučení a postupů, které považuji za nejlepší a nejjednodušší.
HTTP metody
Správné použití HTTP metod je popsáno v odkazovaném článku, zde jen zopakuji, že metoda GET se používá pouze pro získávání dat, metoda POST se nejčastěji používá pro vkládání, PUT pro editaci a DELETE pro mazání.
HTTP kódy
Protokol HTTP obsahuje celou řadu stavových kódů, které se používají pro specifické události.
200 OK
Nejpoužívanější HTTP kód. Je vrácen v případě správného zpracování HTTP požadavku. HTTP odpověď by měla obsahovat další data. Pokud chceme pouze oznámit, že požadavek byl úspěšně zpracován a žádná další data odesílat nechceme, použije se HTTP kód 204. Kód 200 nikdy nesmí být navrácen v případě, že došlo k chybě (často se tak však mylně používá).
201 Created
Používá se při vytvoření nového zdroje. Tedy když úspěšně vytvoříme nový produkt přes POST/PUT /products, vracíme kód 201 a odkaz na nově vytvořený zdroj v hlavičce Location.
202 Accepted
Používá se tehdy, začne-li zpracování nějaké dlouhotrvající operace. Místo toho, abychom čekali na dokončení operace (může trvat velmi dlouho), vrátíme HTTP kód 202. Tím říkáme, že požadavek je validní a jeho zpracování bylo úspěšně zahájeno. Neříká ale nic o tom, jak operace dopadla.
204 No Content
Používá se podobně jako HTTP kód 200, ale nevrací žádný obsah. Používá se např. při mazání zdroje. Když smažeme nějaký produkt, vrátíme kód 204, kterým říkáme, že byl úspěšně z databáze odstraněn.
301 Moved Permanently
Říká, že zdroj byl přesunut na novou adresu. Rozhodneme-li se změnit URL produktu v e-shopu, vrátíme tento kód a hlavičku Location s novou adresou. Prohlížeč by měl po obdržení kódu 301 automaticky aktualizovat všechny reference, takže na původní URL už dotaz nepůjde.
303 See Other
Říká, že požadavek byl zpracován, ale nevrací informace o výsledku zpracování, pouze v hlavičce Location uvádí adresu, kde je možné podrobnější informace o zpracování získat. Hodí se to např. u mobilních aplikací, kdy je potřeba hlídat každý přenesený byte. Nebo pokaždé, když je potřeba přesměrovat z webového formuláře (po POSTu) na nějakou cílovou stránku, třeba po přihlášení.
304 Not Modified
Souvisí s kešováním obsahu. Server tímto kódem říká, že prohlížeč má u sebe aktuální verzi dokumentu a není potřeba, aby stahoval stejnou verzi. Odpověď tedy neobsahuje tělo.
307 Temporary Redirect
Říká, že server aktuálně nemůže zpracovat daný požadavek a místo toho v hlavičce Location posílá adresu, na které má být požadavek o zpracování znovu zaslán.
400 Bad Request
Používá se pro oznámení chyby, na který nelze použít jiný 4×x status.
401 Unauthorized
Neautorizovaný přístup. Uživatel zadal buď chybné přihlašovací údaje, nebo nezadal přihlašovací údaje vůbec.
403 Forbidden
Uživatel požaduje informace o zdroji, ke kterému nemá přístup.
404 Not Found
Uživatel se dotazuje na URL, která neexistuje a nikdy neexistovala. Kód 404 se používá i v situaci, kdy samotné prozrazení existence obsahu na URL má nepříznivý vliv na bezpečnost. Proto je v některých případech legitimní odpovědět místo kódu 401 či 403 tímto kódem.
406 Not Acceptable
Uživatel požaduje vrátit data ve formátu, který server nedokáže obsloužit. Souvisí s hlavičkou Accept, ve které může např. uživatel říct, že chce data ve formátu XML, server je však dokáže odeslat jen ve formátu JSON. Tehdy vrátí kód 406.
409 Conflict
Používá se tehdy, pokud by zpracování požadavku uvedlo aplikaci do nekonzistentního stavu. Chce-li uživatel smazat dokument, na kterém jsou závislé jiné dokumenty, vrátíme tento kód. Může se hodit třeba při požadavku na smazání produktu, který právě někdo nakupuje.
410 Gone
Říkáme, že byl dokument trvale smazán. Pokud víme, že na této adrese něco existovalo, a už to zde není, vracíme 410, nikoliv 404. Na dané adrese by se již nikdy nemělo nic nacházet.
415 Unsupported Media Type
Podobný status jako HTTP 406, ovšem zde říkáme, že uživatel zaslal data ve formátu, který nejsme schopni zpracovat. Např. nám uživatel zaslal data v XML, server ale umí zpracovat jen JSON.
500 Internal Server Error
Chyba je na straně serveru a nesouvisí přímo s obsahem požadavku uživatele. Např. dojde k vyhození výjimky, která nebyla zachycena a zpracována.
HTTP kódů je o něco více, jejich kompletní přehled najdete na české Wikipedii.
Struktura URL
URL má jednoduchý a intuitivní formát a může odkazovat na zdroj, který pracuje buď s celou kolekcí, nebo s konkrétním elementem (dokumentem) kolekce.
Odkaz na celou kolekci produktů může vypadat takto:
example.com/products
Odkaz na konkrétní element produktu s ID 123 takto:
example.com/products/123
Chceme-li se odkázat na dokument, který závisí na jiném dokumentu a nemůže existovat zvlášť, přidáme do cesty i rodičovský dokument. Např. varianty produktů nemohou existovat samostatně, takže na variantu s ID 456 produktu ID 123 se můžeme odkázat takto:
example.com/products/123/variants/456
Kromě práce se zdroji můžeme chtít provádět na kolekci nebo elementu nějakou akci. Např. URL pro vygenerování faktury pro objednávku číslo 123 může mít adresu:
example.com/orders/123/generate
Akce se bude volat metodou HTTP POST, protože žádná akce GET nesmí data měnit.
Názvosloví
Pro názvy kolekcí se používají vždy podstatná jména v množném čísle a nejlépe v anglickém jazyce. Pro názvy akcí na kolekcích či dokumentech se naopak používají slovesa.
Pro oddělení slov v názvech kolekcí či dokumentů se používají pomlčky. Pro názvy atributů se vždy používá tzv. “cammelCase”, tedy např. dateCreated. Podtržítka se nepoužívají nikdy.
Formát komunikace
Nejčastěji se dnes komunikuje přes JSON, který se doporučuje používat jako výchozí formát. Vedle toho se velmi často používá XML. Pokud chce uživatel vrátit odpověď v konkrétním formátu, zasílá se HTTP hlavička Accept.
Filtrování
Pro filtrování výsledků je nejjednodušší přidat požadované filtry do URL, tedy chceme-li např. vrátit produkty s cenou 1000, mohlo by URL to vypadat takto:
example.com/products?price=1000
Stránkování
Pro stránkování lze přidat do URL parametr limit (říká, kolik záznamů se má vrátit) a offset (od kterého záznamu). Tedy chceme-li vrátit 25 produktů od prvního záznamu, můžeme použit tento dotaz:
example.com/products?limit=25&offset=0
Zde je potřeba upozornit, že je vždy nutné validovat požadovaný rozsah a nastavit maximální rozsah, který můžeme vrátit.
Kromě toho je možné pro stránkování použít HTTP hlavičku Range a v odpovědi Content-Range.
Řazení
Pro řazení se použije v URL parametr sort, který obsahuje pole, podle kterých se mají dokumenty seřadit. Pokud chceme použít opačné pořadí, před název pole se vloží znak “-”.
Chcete-li tedy vrátit všechny produkty podle jejich ceny (price) a dále v opačném pořadí podle názvu (name), pak dotaz může vypadat takto:
example.com/products?sort=price,-name
Pole
Chceme-li vrátit jen určitá pole (sloupce), použije se parametr fields. Chceme-li tedy vrátit název produktu, perex a cenu, mohl by dotaz vypadat takto:
example.com/products?fields=name,perex,price
Verzování API
API je dobré hned od začátku verzovat a nejčastěji se používá prefix v + číslo verze. Takže např. kolekce s produkty bude dostupná na adrese:
example.com/v1/products
Umístění API
Nejčastěji se umisťuje na samostatnou subdoménu api, tedy např. api.example.com. V případě naší aplikace pro zjednodušení nebudeme vytvářet samostatnou subdoménu a API bude dostupné přes prefix /api, tedy produkty získáme na adrese:
example.com/api/v1/products
Dokumentace API
Dokumentace k API se doporučuje umísťovat na samostatnou subdoménu developers: developers.example.com.
Kromě toho je dobré přidat přesměrování z adres dev.example.com a developer.example.com.
Zpracování chyb
Nastane-li chyba, v HTTP odpovědi zasíláme vždy parametr message s popisem chyby. Volitelně pak parametr code (interní číslo chyby) nebo type (pojmenování chyby) a odkaz na podrobnější informace v parametru moreInfo. Tedy zadá-li např. uživatel v dotazu, že chce vrátit pole “perex”, které však nevracíme, vrátíme HTTP kód 400 s tímto popisem chyby:
{ code: 1234, message: "Požadované pole 'perex' neexistuje.", moreInfo: "http://developers.example.com/errors/1234" }
Co dále
V příštím díle dnes zmíněnou teorii uvedeme do praxe a upravíme e-shop tak, aby zmíněná pravidla plně respektoval.
Na tvorbě tohoto článku se svými připomínkami podílel také Pavel Lang (github). Díky!
Přehled komentářů