Návrh webového rozhraní RESTful API

Většina moderních webových aplikací zveřejňuje rozhraní API, které můžou klienti používat k interakci s aplikací. Dobře navržené webové rozhraní API by mělo být zaměřené na podporu následujících aspektů:

  • Nezávislost platformy. Rozhraní API by měl být schopný volat jakýkoli klient bez ohledu na to, jak je rozhraní API implementované interně. To vyžaduje použití standardních protokolů a přijetí mechanismu, pomocí kterého se klient a webová služba můžou dohodnout na formátu vyměňovaných dat.

  • Vývoj služby. Webové rozhraní API by mělo mít možnost se vyvíjet a přidávat funkce nezávisle na klientských aplikacích. Jak se rozhraní API vyvíjí, existující klientské aplikace by měly být nadále schopné fungovat bez úprav. Všechny funkce by měly být zjistitelné, aby je klientské aplikace mohly plně používat.

Tento návod popisuje problémy, které byste měli při návrhu webového rozhraní API vzít v úvahu.

Co je REST?

V roce 2000 Roy Fielding navrhl REST (Representational State Transfer) jako architektonický přístup k navrhování webových služeb. REST je styl architektury pro vytváření distribuovaných systémů založených na hypermédiích. REST je nezávislý na všech podřízených protokolech a není nutně svázán s HTTP. Nejběžnější implementace rozhraní REST API však používají protokol HTTP jako aplikační protokol a tato příručka se zaměřuje na návrh rozhraní REST API pro HTTP.

Hlavní výhodou REST oproti protokolu HTTP je, že používá otevřené standardy a neváže implementaci rozhraní API ani klientských aplikací k žádné konkrétní implementaci. Například webová služba REST může být napsaná v ASP.NET a klientské aplikace můžou používat libovolný jazyk nebo sadu nástrojů, které dovedou generovat požadavky HTTP a parsovat odpovědi HTTP.

Tady jsou některé z hlavní zásad rozhraní RESTful API pomocí protokolu HTTP:

  • Rozhraní REST API jsou navržená kolem prostředků, což je jakýkoli druh objektu, dat nebo služby, ke kterým může klient přistupovat.

  • Prostředek má identifikátor, což je identifikátor URI, který jednoznačně tento prostředek identifikuje. Například identifikátor URI pro konkrétní objednávku zákazníka může být následující:

    https://adventure-works.com/orders/1
    
  • Klienti komunikují se službou tak, že si vyměňují reprezentace prostředků. Mnoho webových rozhraní API používá jako formát výměny JSON. Například požadavek GET na identifikátor URI uvedený výš může vrátit toto tělo odpovědi:

    {"orderId":1,"orderValue":99.90,"productId":1,"quantity":1}
    
  • Rozhraní REST API používají jednotné rozhraní, které pomáhá oddělit implementace klienta a služby. Pro rozhraní REST API založená na protokolu HTTP zahrnuje jednotné rozhraní použití standardních příkazů HTTP k provádění operací s prostředky. Nejběžnější operace jsou GET, POST, PUT, PATCH a DELETE.

  • Rozhraní REST API používají bezstavový model požadavků. Požadavky HTTP by měly být nezávislé a můžou nastat v libovolném pořadí, takže udržování přechodných informací o stavu mezi požadavky není možné. Jediným místem, kde se informace ukládají, jsou samotné prostředky a každý požadavek by měl být atomickou operací. Toto omezení umožňuje webovým službám být vysoce škálovatelné, protože není nutné zachovávat žádné vztahy mezi klienty a konkrétními servery. Každý server může zpracovat jakýkoli požadavek od jakéhokoli klienta. Je ale nutné dodat, že škálovatelnost můžou omezit jiné faktory. Mnoho webových služeb například zapisuje do back-endového úložiště dat, což může být obtížné škálovat na více instancí. Další informace o strategiích škálování úložiště dat najdete v tématu Horizontální, vertikální a funkční dělení dat.

  • Rozhraní REST API se řídí hypermediálními odkazy, které jsou obsaženy v reprezentaci. Následující příklad ukazuje reprezentaci objednávky JSON. Obsahuje odkazy pro získání nebo aktualizaci zákazníka přidruženého objednávce.

    {
      "orderID":3,
      "productID":2,
      "quantity":4,
      "orderValue":16.60,
      "links": [
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"GET" },
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"PUT" }
      ]
    }
    

V roce 2008 Leonard Richardson navrhl následující model vyspělosti pro webová rozhraní API:

  • Úroveň 0: Definujte jeden identifikátor URI a všechny operace jako požadavky POST na tento identifikátor URI.
  • Úroveň 1: Vytvořte oddělené identifikátory URI pro jednotlivé prostředky.
  • Úroveň 2: K definování operací s prostředky používejte metody HTTP.
  • Úroveň 3: Používejte hypermédia (HATEOAS, viz dál).

Úroveň 3 odpovídá skutečnému rozhraní RESTful API podle definice Fieldinga. V praxi mnoho publikovaných webových rozhraní API spadají někam kolem úrovně 2.

Uspořádání návrhu rozhraní API kolem prostředků

Soustřeďte se na obchodní entity, které webové rozhraní API zveřejňuje. V systému elektronického obchodování můžou být primárními entitami například zákazníci a objednávky. Vytvoření objednávky lze dosáhnout odesláním požadavku HTTP POST, který obsahuje informace o objednávce. Odpověď HTTP označuje, zda byla objednávka zadána úspěšně, nebo ne. Pokud je to možné, identifikátory URI by měly vycházet z podstatných jmen (prostředků), a ne sloves (operací s prostředkem).

https://adventure-works.com/orders // Good

https://adventure-works.com/create-order // Avoid

Prostředek nemusí být založený na jedné fyzické položce dat. Prostředek objednávky může být například implementován interně jako několik tabulek v relační databázi, ale zobrazuje se klientovi jako jedna entita. Vyhněte se vytváření rozhraní API, která jednoduše zrcadlí interní strukturu databáze. Účelem REST je modelovat entity a operace, které aplikace může s těmito entitami provádět. Klientovi by neměla být zveřejněna interní implementace.

Entity jsou často seskupené do kolekcí (objednávky, zákazníci). Kolekce je samostatný prostředek, který nesouvisí s položkou v kolekci a musí mít svůj vlastní identifikátor URI. Následující identifikátor URI může například představovat kolekci objednávek:

https://adventure-works.com/orders

Odeslání požadavku HTTP GET identifikátoru URI kolekce načte seznam položek v kolekci. Každá položka v kolekci má také svůj vlastní jedinečný identifikátor URI. Požadavek HTTP GET na identifikátor URI položky vrátí podrobnosti o této položce.

Přijměte konzistentní zásady vytváření názvů pro identifikátory URI. Obecně platí, že je dobré pro identifikátory URI odkazující na kolekce používat podstatná jména v množném čísle. Je dobrým zvykem organizovat identifikátory URI pro kolekce a položky do hierarchie. Například /customers je cesta ke kolekci zákazníků a /customers/5 je cesta k zákazníkovi s ID hodnoty 5. Tento přístup pomáhá, aby webové rozhraní API bylo intuitivní. Navíc mnoho architektur webových rozhraní API může směrovat požadavky na parametrizované cesty identifikátorů URI, takže můžete třeba definovat trasu pro cestu /customers/{id}.

Zvažte také vztahy mezi různými typy prostředků a jak je možné tato přidružení zveřejnit. Například /customers/5/orders může představovat všechny objednávky zákazníka číslo 5. Můžete také postupovat opačným směrem a reprezentovat přidružení od objednávky zpátky k zákazníkovi pomocí identifikátoru URI, jako je /orders/99/customer. Rozšíření tohoto model příliš daleko ale může vést k poněkud těžkopádné implementaci. Lepším řešením je poskytnout v těle zprávy s odpovědí HTTP odkazy na související prostředky s možností navigace. Tento mechanismus je podrobněji popsán v části Použití HATEOAS k povolení navigace na související prostředky.

Ve složitějších systémech může být lákavé poskytovat identifikátory URI, které umožní klientovi procházet několik úrovní relací, jako například /customers/1/orders/99/products. Tuto úroveň složitosti ale může být poměrně obtížné udržovat a není flexibilní, pokud se v budoucnu vztahy mezi prostředky změní. Raději zkuste udržovat identifikátory URI poměrně jednoduché. Jakmile aplikace obsahuje odkaz na prostředek, mělo by být možné použít tento odkaz k nalezení položek souvisejících s tímto prostředkem. Předchozí dotaz lze nahradit identifikátorem URI /customers/1/orders a vyhledat všechny objednávky pro zákazníka 1, a potom pomocí /orders/99/products najít produkty v této objednávce.

Tip

Vyhněte se nutnosti používat identifikátory URI prostředků složitější než kolekce/položka/kolekce.

Dalším faktorem je, že všechny webové požadavky vytvářejí zatížení webového serveru. Čím je požadavků víc, tím je zatížení větší. Proto se pokuste vyhnout příliš „upovídaným“ webovým rozhraním API, která zveřejňují velké množství malých prostředků. Takové rozhraní API může vyžadovat, aby klientská aplikace odeslala více požadavků, aby našla všechna data, která vyžaduje. Místo toho se můžete snažit denormalizovat data a kombinovat související informace do větších prostředků, které jde načíst jedním požadavkem. Je ale potřeba tento přístup vyvážit s režií při načítání dat, která klient nepotřebuje. Načítání velkých objektů můžete zvýšit latenci požadavku a generovat další náklady na šířku pásma. Další informace o těchto antivzorech výkonu najdete v tématech Přetížení vstupně-výstupních operací a Nadbytečné načítání.

Vyhněte se zavedení závislostí mezi webovým rozhraním API a podkladovými zdroji dat. Například pokud jsou vaše data uložená v relační databázi, webové rozhraní API nemusí nutně zveřejňovat každou tabulku jako kolekci prostředků. Ve skutečnosti je to pravděpodobně špatný návrh. Místo toho přemýšlejte o webovém rozhraní API jako o abstrakci databáze. V případě potřeby zaveďte vrstvu mapování mezi databází a webovým rozhraním API. Tím způsobem jsou klientské aplikace izolované od změn schématu podkladové databáze.

Navíc nemusí být možné mapovat každou operaci implementovanou webovým rozhraním API na konkrétní prostředek. Můžete například zpracovávat takové neprostředkové scénáře, kdy se pomocí požadavku HTTP vyvolá funkce a výsledek se vrátí jako zpráva s odpovědí HTTP. Webové rozhraní API, které implementuje jednoduché operace kalkulačky, jako je sčítání a odečítání, může například poskytovat identifikátory URI, které zveřejňují tyto operace jako pseudoprostředky a používají řetězec dotazu k určení požadovaných parametrů. Například požadavek GET na identifikátor URI /add?operand1=99&operand2=1 by vrátil zprávu odpovědi s textem obsahujícím hodnotu 100. Tyto tvary identifikátorů URI ale používejte přiměřeně.

Definování operací rozhraní API z hlediska metod HTTP

Protokol HTTP definuje několik metod, které žádosti přiřazují sémantický význam. Běžné metody HTTP, které používá většina webových rozhraní RESTful API, jsou následující:

  • GET načte reprezentaci prostředku se zadaným identifikátorem URI. Tělo zprávy s odpovědí obsahuje podrobnosti o požadovaném prostředku.
  • POST vytvoří nový prostředek se zadaným identifikátorem URI. Tělo zprávy požadavku obsahuje podrobnosti o novém prostředku. Metodu POST jde také použít k aktivaci operací, které ve skutečnosti nevytváří prostředky.
  • PUT vytvoří nebo nahradí prostředek se zadaným identifikátorem URI. Tělo zprávy požadavku určuje prostředek, který se má vytvořit nebo aktualizovat.
  • PATCH provede částečnou aktualizaci prostředku. Tělo požadavku určuje sadu změn, které se mají použít na prostředek.
  • DELETE odebere prostředek se zadaným identifikátorem URI.

Účinek konkrétního požadavku by měl záviset na tom, jestli je prostředek kolekce, nebo jednotlivá položka. Následující tabulka shrnuje běžné konvence přijaté většinou implementací RESTful pomocí příkladu elektronického obchodování. Ne všechny tyto požadavky se můžou implementovat – závisí na konkrétním scénáři.

Prostředek POST GET PUT DELETE
/customers Vytvoření nového zákazníka Načtení všech zákazníků Hromadná aktualizace zákazníků Odebrání všech zákazníků
/customers/1 Chyba Načtení podrobností pro zákazníka 1 Aktualizace podrobností pro zákazníka 1, pokud existuje Odebrání zákazníka 1
/customers/1/orders Vytvoření nové objednávky pro zákazníka 1 Načtení všech objednávek pro zákazníka 1 Hromadná aktualizace objednávek pro zákazníka 1 Odebrání všech objednávek pro zákazníka 1

Rozdíly mezi metodami POST, PUT a PATCH můžou být matoucí.

  • Požadavek POST vytvoří prostředek. Server přiřadí novému prostředku identifikátor URI a vrátí tento identifikátor URI klientovi. V modelu REST se často používají požadavky POST na kolekce. Nový prostředek se přidá do kolekce. Požadavek POST jde také použít k odeslání dat ke zpracování pro existující prostředek, bez jakéhokoli vytváření nového prostředku.

  • Požadavek PUT vytvoří prostředek nebo aktualizuje existující prostředek. Klient určuje identifikátor URI pro prostředek. Tělo požadavku obsahuje úplnou reprezentaci prostředku. Pokud prostředek s tímto identifikátorem URI už existuje, nahradí se. V opačném případě se vytvoří nový prostředek, pokud to server podporuje. Požadavky PUT se nejčastěji používají na prostředky, které jsou jednotlivými položkami, jako je například konkrétní zákazník, nikoli kolekce. Server může prostřednictvím požadavku PUT podporovat aktualizace, ale ne vytvoření. Podpora vytváření prostřednictvím požadavků PUT závisí na tom, jestli klient může smysluplně přiřadit identifikátor URI prostředku před jeho existencí. Pokud ne, pak k vytváření prostředků používejte POST a pomocí PUT nebo PATCH aktualizujte.

  • Požadavek PATCH provede částečnou aktualizaci existujícího prostředku. Klient určuje identifikátor URI pro prostředek. Tělo požadavku určuje sadu změn, které se mají použít na prostředek. To může být efektivnější než použití PUT, protože klient odešle jenom změny, ne celou reprezentaci prostředku. Technicky může PATCH taky vytvořit nový prostředek (zadáním sady aktualizací na „nulový“ prostředek), pokud to server podporuje.

Požadavky PUT musí být idempotentní. Pokud klient odešle stejný požadavek PUT víckrát, výsledky by měly být vždycky stejné (stejný prostředek bude upravený pomocí stejných hodnot). U požadavků POST a PATCH není idempotence zaručená.

Soulad se sémantikou HTTP

Tato část popisuje některé typické aspekty návrhu rozhraní API, které splňuje specifikace protokolu HTTP. Nezahrnuje ale všechny možné podrobnosti nebo scénáře. V případě pochybností si přečtěte specifikaci protokolu HTTP.

Typy médií

Jak už bylo zmíněno dříve, klienti a servery si vyměňují reprezentace prostředků. Například v požadavku POST obsahuje tělo požadavku reprezentaci vytvářeného prostředku. V požadavek GET obsahuje tělo odpovědi reprezentaci načítaného prostředku.

V protokolu HTTP jsou formáty určené prostřednictvím typů médií, označovaných taky jako typy MIME. U jiných než binárních dat podporuje většina webových rozhraní API JSON (typ média = application/json) a případně XML (typ média = application/xml).

Hlavička Content-Type v požadavku nebo odpovědi určuje formát reprezentace. Tady je příklad požadavku POST, který obsahuje data JSON:

POST https://adventure-works.com/orders HTTP/1.1
Content-Type: application/json; charset=utf-8
Content-Length: 57

{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

Pokud server nepodporuje typ média, měl by vrátit stavový kód HTTP 415 (Nepodporovaný typ média).

Požadavek klienta může obsahovat hlavičku Accept, která obsahuje seznam typů médií, které klient ve zprávě s odpovědí ze serveru přijme. Příklad:

GET https://adventure-works.com/orders/2 HTTP/1.1
Accept: application/json

Pokud server nemůže odpovídat žádnému z uvedených typů médií, měl by vrátit stavový kód HTTP 406 (Není přijatelný).

Metody GET

Úspěšná metoda GET obvykle vrátí stavový kód HTTP 200 (OK). Pokud se prostředek nenašel, metoda by měla vrátit kód 404 (Nenalezeno).

Pokud byl požadavek splněn, ale v odpovědi HTTP neexistuje žádný text odpovědi, měl by vrátit stavový kód HTTP 204 (bez obsahu); Například operace vyhledávání s výsledkem, že se tímto chováním nedají implementovat žádné shody.

Metody POST

Pokud metoda POST vytvoří nový prostředek, vrátí stavový kód HTTP 201 (Vytvořeno). Identifikátor URI nového prostředku je součástí hlavičky Location v odpovědi. Tělo odpovědi obsahuje reprezentaci prostředku.

Pokud metoda provede nějaké zpracování, ale nevytvoří nový prostředek, můžete metoda vrátit stavový kód HTTP 200 a zahrnout výsledek operace do těla odpovědi. Případně pokud neexistuje žádný výsledek určený k vrácení, metoda může vrátit stavový kód HTTP 204 (Bez obsahu) s žádnou odpovědí.

Pokud klient vloží do požadavku neplatná data, server by měl vrátit stavový kód HTTP 400 (Chybný požadavek). Tělo odpovědi může obsahovat další informace o chybě nebo odkaz na identifikátor URI, který obsahuje další podrobnosti.

Metody PUT

Pokud metoda PUT vytvoří nový prostředek, vrátí stavový kód HTTP 201 (Vytvořeno), stejně jako metoda POST. Pokud metoda aktualizuje existující prostředek, vrátí hodnotu 200 (OK) nebo 204 (Bez obsahu). V některých případech nemusí být možné aktualizovat existující prostředek. V takovém případě zvažte vrácení stavového kódu protokolu HTTP 409 (Konflikt).

Zvažte implementaci hromadné operace HTTP PUT, která může hromadně aktualizovat víc prostředků v kolekci. Požadavek PUT musí určovat identifikátor URI kolekce a tělo požadavku musí určovat podrobnosti o měněných prostředcích. Tento přístup může pomoct snížit upovídanost a zlepšit výkon.

Metody PATCH

S požadavkem PATCH klient odešle sadu aktualizací pro existující prostředek ve tvaru dokumentu opravy. Server zpracuje dokument opravy a provede aktualizaci. Dokument opravy nepopisuje celý prostředek, ale jen sadu změn k použití. Specifikace pro metodu PATCH (RFC 5789) nedefinuje konkrétní formát pro dokumenty opravy. Formát musí být odvozený od typu média v požadavku.

JSON je pravděpodobně nejběžnější formát dat pro webová rozhraní API. Existují dva hlavní formáty oprav založené na JSON, oprava JSON a sloučená oprava JSON.

Sloučená oprava JSON je poněkud jednodušší. Dokument opravy má stejnou strukturu jako původní prostředek JSON, ale zahrnuje jen dílčí sadu polí, která by se měla změnit nebo přidat. Kromě toho lze v dokumentu opravy pole odstranit zadáním hodnoty pole null. (To znamená, že sloučená oprava není vhodná, pokud původní prostředek může mít explicitní hodnoty null.)

Předpokládejme například, že původní prostředek má následující reprezentaci JSON:

{
    "name":"gizmo",
    "category":"widgets",
    "color":"blue",
    "price":10
}

Tady je možná sloučená oprava JSON pro tento prostředek:

{
    "price":12,
    "color":null,
    "size":"small"
}

To serveru říká, aby aktualizoval price, odstranil colora přidal size, zatímco name a category nejsou změněny. Podrobnosti o sloučené opravě JSON najdete v dokumentu RFC 7396. Typ média pro opravu sloučení JSON je application/merge-patch+json.

Sloučená oprava není vhodná, pokud původní prostředek může obsahovat explicitní hodnoty null, z důvodu zvláštního významu hodnoty null v dokumentu opravy. Dokument opravy také neurčuje pořadí, v jakém má server provádět aktualizace. To může nebo nemusí vadit, v závislosti na datech a doméně. Oprava JSON, definovaná v dokumentu RFC 6902, je flexibilnější. Určuje změny jako posloupnost operací, které se mají použít. Operace zahrnují přidání, odebrání, náhradu, kopírování a testování (k ověření hodnot). Typ média pro opravu JSON je application/json-patch+json.

Tady jsou uvedené některé typické chybové stavy, které můžou nastat při zpracování požadavku PATCH, spolu s odpovídajícím stavovým kódem HTTP.

Chybový stav Kód stavu HTTP
Formát dokument opravy není podporovaný. 415 (Nepodporovaný typ média)
Chybný formát dokumentu opravy. 400 (Chybný požadavek)
Dokumentu opravy je platný, ale změny nejde použít na prostředek v jeho aktuálním stavu. 409 (Konflikt)

Metody DELETE

Pokud je operace odstranění úspěšná, webový server by měl odpovědět stavovým kódem HTTP 204 (Bez obsahu), který označuje, že proces byl úspěšně zpracován, ale text odpovědi neobsahuje žádné další informace. Pokud prostředek neexistuje, může webový server vrátit chybu HTTP 404 (Nenalezeno).

Asynchronních operace

Někdy může operace POST, PUT, PATCH nebo DELETE vyžadovat zpracování, které nějakou dobu trvá. Pokud před odesláním odpovědi klientovi počkáte na dokončení, může to způsobit nepřijatelnou latenci. V takovém případě zvažte provedení operace asynchronně. Vraťte stavový kód HTTP 202 (Přijato) k označení, že žádost byla přijata ke zpracování, ale není dokončená.

Je potřeba zveřejnit koncový bod, který vrací stav asynchronního požadavku, aby klient mohl sledovat stav pomocí cyklického dotazování na koncový bod stavu. Zahrňte identifikátor URI koncového bodu stavu do hlavičky Location odpovědi 202. Příklad:

HTTP/1.1 202 Accepted
Location: /api/status/12345

Pokud klient odešle požadavek GET na tento koncový bod, odpověď by měla obsahovat aktuální stav požadavku. Volitelně může obsahovat také odhadovaný čas dokončení nebo odkaz na zrušení operace.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "status":"In progress",
    "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" }
}

Pokud asynchronní operace vytvoří nový prostředek, koncový bod stavu by měl po dokončení operace vrátit stavový kód 303 (Zobrazit jiné). V odpovědi 303 uveďte hlavičku Location, která obsahuje identifikátor URI nového prostředku:

HTTP/1.1 303 See Other
Location: /api/orders/12345

Další informace o tom, jak tento přístup implementovat, najdete v tématu Poskytování asynchronní podpory pro dlouhotrvající požadavky a vzor Asynchronní požadavek-odpověď.

Prázdné sady v těle zprávy

Kdykoli je text úspěšné odpovědi prázdný, stavový kód by měl být 204 (Bez obsahu). Pro prázdné sady, například odpověď na filtrovaný požadavek bez položek, by stavový kód měl být stále 204 (Bez obsahu), ne 200 (OK).

Filtrování a stránkování dat

Zveřejnění kolekce prostředků prostřednictvím jednoho identifikátoru URI může vést k tomu, že aplikace načítají velké objemy dat, i když potřebují jenom část informací. Předpokládejme například, že klientská aplikace potřebuje najít objednávky s cenou vyšší než daná hodnota. Může načíst všechny objednávky z identifikátoru URI /orders a pak tyto objednávky vyfiltrovat na straně klienta. Tento proces je zjevně vysoce neefektivní. Zbytečně plýtvá šířkou pásma a výkonem serveru, který je hostitelem webového rozhraní API.

Místo toho může rozhraní API umožnit předání filtru v řetězci dotazu identifikátoru URI, například /orders?minCost=n. Webové rozhraní API pak zodpovídá za parsování a zpracování parametru minCost v řetězci dotazu a vrácení filtrovaných výsledků na straně serveru.

Požadavky GET na prostředky kolekce můžou potenciálně vrátit velký počet položek. Měli byste navrhnout webové rozhraní API tak, aby omezovalo množství dat vrácených každým jednotlivým požadavkem. Zvažte podporu řetězců dotazu, které určují maximální počet položek k načtení a počáteční posun v kolekci. Příklad:

/orders?limit=25&offset=50

Zvažte taky nastavení horního limitu počtu vrácených položek, aby se zabránilo útokům typu Denial of Service. Požadavky GET, které vracejí stránkovaná data, by měly pomáhat klientským aplikacím tak, že budou také obsahovat určitou formu metadata indikující celkový počet prostředků dostupných v kolekci.

Podobné strategie můžete použít k řazení dat během jejich načítání tím, že zavedete parametr řazení, který přebírá název pole jako hodnotu, například /orders?sort=ProductID. Tento postup však může mít negativní vliv na ukládání do mezipaměti, protože parametry řetězce dotazu tvoří část identifikátoru prostředku, který mnoho implementací mezipaměti používá jako klíč pro uložená data.

Tento přístup můžete rozšířit, abyste omezili počet polí vracených pro každou položku, pokud každá položka obsahuje velké množství dat. Můžete například použít parametr řetězce dotazu, který přijímá čárkami oddělený seznam polí, například /orders?fields=ProductID,Quantity.

Dejte všem volitelným parametrům v řetězcích dotazů smysluplné výchozí hodnoty. Například nastavte parametr limit na hodnotu 10 a parametr offset na hodnotu 0, pokud implementujete stránkování, nastavte parametr řazení na klíč prostředku, pokud implementujete řazení, a nastavte parametr fields na všechna pole v prostředku, pokud podporujete projekce.

Podpora částečných odpovědí pro velké binární prostředky

Prostředek může obsahovat velká binární pole, například soubory nebo obrázky. Abyste vyřešili problémy způsobené nespolehlivým a přerušovaným připojením a vylepšili dobu odezvy, zvažte, jestli u těchto prostředků nepovolit načítání v blocích. K tomuto účelu by webové rozhraní API mělo pro požadavky GET pro velké prostředky podporovat hlavičku Accept-Ranges. Tyto hlavička označuje, že operace GET podporuje částečné požadavky. Klientská aplikace může odesílat požadavky GET, které vrátí podmnožinu prostředku, určenou jako rozsah bajtů.

Zvažte také pro tyto prostředky implementaci požadavku HTTP HEAD. Požadavek HEAD je podobný požadavku GET s tím rozdílem, že vrátí jenom hlavičky HTTP, které prostředek popisují, s prázdným tělem zprávy. Klientská aplikace může vydat požadavek HEAD k určení, jestli se má prostředek načíst pomocí částečných požadavků GET. Příklad:

HEAD https://adventure-works.com/products/10?fields=productImage HTTP/1.1

Tady je ukázková zpráva odpovědi:

HTTP/1.1 200 OK

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 4580

Hlavička Content-Length dává celkovou velikost prostředku a hlavička Accept-Ranges označuje, že odpovídající operace GET podporuje částečné výsledky. Klientská aplikace můžete použít tyto informace k načtení obrázku po menších částech. První požadavek načte prvních 2 500 bajtů pomocí hlavičky Range:

GET https://adventure-works.com/products/10?fields=productImage HTTP/1.1
Range: bytes=0-2499

Zpráva odpovědi označuje, že se jedná o částečnou odpověď, vrácením stavového kódu HTTP 206. Hlavička Content-Length určuje skutečný počet bajtů vrácených v tělu zprávy (nikoli velikost prostředku), a hlavička Content-Range označuje, o kterou část prostředku se jedná (bajty 0 až 2499 z 4580):

HTTP/1.1 206 Partial Content

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 2500
Content-Range: bytes 0-2499/4580

[...]

Následná žádost z klientské aplikace může načíst zbytek prostředku.

Jednou z primárních motivací REST je, aby bylo možné projít celou sadu prostředků bez nutnosti předchozí znalosti schématu identifikátorů URI. Každý požadavek HTTP GET by měl vrátit informace potřebné k vyhledání prostředků souvisejících přímo s vyžádaným objektem prostřednictvím hypertextových odkazů obsažených v odpovědi a měl by také dostat informace, které popisují operace dostupné pro každý z těchto prostředků. Tento princip se označuje zkratkou HATEOAS (Hypertext as the Engine of Application State). Systém je efektivně konečný automat a odpověď na každý požadavek obsahuje informace potřebné k přesunu z jednoho stavu do jiného; žádné další informace by měly být nutné.

Poznámka:

V současné době neexistují žádné standardy pro obecné účely, které definují, jak modelovat princip HATEOAS. Příklady uvedené v této části ilustrují jedno možné proprietární řešení.

Pro zpracování relace mezi objednávkou a zákazníkem může například reprezentace objednávky zahrnovat odkazy, které určují dostupné operace pro zákazníka objednávky. Tady je možná reprezentace:

{
  "orderID":3,
  "productID":2,
  "quantity":4,
  "orderValue":16.60,
  "links":[
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"DELETE",
      "types":[]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"DELETE",
      "types":[]
    }]
}

V tomto příkladu pole links obsahuje sadu odkazů. Každý odkaz představuje operaci se související entitou. Data pro každý odkaz zahrnují relaci (customer), identifikátor URI (https://adventure-works.com/customers/3), metodu HTTP a podporované typy MIME. Toto jsou veškeré informace, které klientská aplikace potřebuje k vyvolání operace.

Pole links obsahuje také informace s odkazem na vlastní prostředek, který byl načten. Ty mají relaci self.

Sada odkazů, které jsou vrácené, se může změnit v závislosti na stavu prostředku. Toto je naplnění pojmu „Hypertext as the Engine of Application State“ (Hypertext jako aplikační stav).

Správa verzí webového rozhraní RESTful API

Je vysoce nepravděpodobné, že webové rozhraní API zůstane statické. Se změnami obchodních požadavků můžou být přidány nové kolekce prostředků, můžou se změnit relace mezi prostředky a může se upravit struktura dat v prostředcích. Zatímco aktualizace webového rozhraní API, aby zpracovávalo nové nebo odlišné požadavky, je poměrně přímočarý proces, musíte zvážit důsledky, které tyto změny budou mít na klientské aplikace využívající webové rozhraní API. Problémem je, že i když vývojář návrh a implementace webového rozhraní API má plnou kontrolu nad tímto rozhraním API, vývojář nemá stejný stupeň kontroly nad klientskými aplikacemi, které mohou být vytvořeny organizacemi třetích stran, které pracují vzdáleně. Primární pravidlo je umožnit existujícím klientským aplikacím, aby fungovaly beze změny, a současně umožnit novým klientským aplikacím, aby mohly využívat nové funkce a prostředky.

Správa verzí umožňuje webovému rozhraní API označovat funkce a prostředky, které zveřejňuje, a klientské aplikace můžou odesílat požadavky, které jsou směrované na konkrétní verzi funkce nebo prostředku. Následující části popisují několik různých přístupů, kdy každý má své výhody a kompromisy.

Bez správy verzí

Toto je nejjednodušší přístup a může být přijatelný pro některá interní rozhraní API. Významné změny můžou být reprezentovány jako nové zdroje nebo nové odkazy. Přidání obsahu do existujících prostředků nemusí představovat zásadní změnu, protože klientské aplikace, které neočekávají, že tento obsah budou ignorovat.

Například požadavek na identifikátor URI https://adventure-works.com/customers/3 by měl vrátit podrobnosti jednoho zákazníka, který obsahuje id, namea address pole očekávaná klientskou aplikací:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Poznámka:

Pro jednoduchost ukázkové odpovědi uvedené v této části nezahrnují odkazy HATEOAS.

Pokud se do schématu prostředku zákazníka přidá pole DateCreated, bude odpověď vypadat takto:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":"1 Microsoft Way Redmond WA 98053"}

Existující klientské aplikace můžou dál fungovat správně, pokud jsou schopné ignorovat nerozpoznaná pole, zatímco nové klientské aplikace můžou být navržené tak, aby toto nové pole zapracovávaly. Ale pokud dojde k radikálnější změně schématu prostředků (například odebrání nebo přejmenování polí) nebo ke změně relací mezi prostředky, pak můžou představovat velké změny, které zabraňují existujícím klientským aplikacím fungovat správně. V těchto situacích byste měli zvážit jeden z následujících přístupů.

Správa verzí pomocí identifikátoru URI

Pokaždé, když upravíte webové rozhraní API nebo změníte schéma prostředků, přidejte číslo verze do identifikátoru URI každého prostředku. Dříve existující identifikátory URI by měly nadále fungovat jako předtím a vracet prostředky, které odpovídají jejich původnímu schématu.

Rozšíření předchozího příkladu, pokud address je pole restrukturalizováno do dílčích polí obsahujících každou součást adresy (například streetAddress, city, statea zipCode), může být tato verze prostředku zpřístupněna prostřednictvím identifikátoru URI obsahujícího číslo verze, například https://adventure-works.com/v2/customers/3:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Tento mechanismus správy verzí je velmi jednoduchý, ale závisí na směrování požadavků serverem na příslušný koncový bod. Během času, jak se webové rozhraní API vyvíjí prostřednictvím několika iterací a server musí podporovat několik různých verzí, se ale může stát těžkopádným. Z hlediska puristy se také ve všech případech klientské aplikace načítají stejná data (zákazník 3), takže identifikátor URI by se neměl v závislosti na verzi ve skutečnosti lišit. Toto schéma také komplikuje implementaci HATEOAS, protože všechny odkazy musí v identifikátorech URI obsahovat číslo verze.

Správa verzí pomocí řetězce dotazu

Místo poskytování více identifikátorů URI můžete zadat verzi prostředku pomocí parametru v řetězci dotazu připojeného k požadavku HTTP, například https://adventure-works.com/customers/3?version=2. Parametr verze by měl mít smysluplnou výchozí hodnotu, například 1, pokud ho starší klientské aplikace neuvádějí.

Tento přístup má sémantickou výhodu, že stejný prostředek se vždycky načítá pomocí stejného identifikátoru URI, ale závisí na kódu, který zpracovává požadavek, aby parsoval řetězec dotazu a odeslal zpátky odpovídající odpověď HTTP. Tento přístup také vykazuje stejné komplikace při implementaci HATEOAS jako mechanismus správy verzí pomocí identifikátoru URI.

Poznámka:

Některé starší webové prohlížeče a webové proxy servery nebudou ukládat do mezipaměti odpovědi pro požadavky, které obsahují řetězec dotazu v identifikátoru URI. To může snížit výkon webových aplikací, které používají webové rozhraní API a které běží v rámci takového webového prohlížeče.

Správa verzí pomocí hlavičky

Místo přidávání čísla verze jako parametru řetězce dotazu může implementovat vlastní hlavičku, která určuje verzi prostředku. Tento přístup vyžaduje, aby klientská aplikace přidala odpovídající hlavičku do všech požadavků, i když kód, který zpracovává požadavek klienta, může použít výchozí hodnotu (verze 1), když je hlavička verze vynechaná. Následující příklady používají vlastní hlavičku s názvem Custom-Header. Hodnotu této hlavičky určuje verzi webového rozhraní API.

Verze 1:

GET https://adventure-works.com/customers/3 HTTP/1.1
Custom-Header: api-version=1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Verze 2:

GET https://adventure-works.com/customers/3 HTTP/1.1
Custom-Header: api-version=2
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Stejně jako u předchozích dvou přístupů vyžaduje implementace HATEOAS do všech odkazů odpovídající vlastní hlavičku.

Správa verzí pomocí typu média

Když klientská aplikace odesílá požadavek HTTP GET na webový server, měla by specifikovat formát obsahu, který dokáže zpracovat, pomocí hlavičky Accept, jak je popsáno výše v tomto návodu. Často účel hlavičky Accept je umožnit klientské aplikaci určit, zda má být tělo odpovědi XML, JSON nebo některý jiný běžný formát, který umí klient parsovat. Nicméně je možné definovat vlastní typy médií, které obsahují informace umožňující klientské aplikaci označit, která verze prostředku se očekává.

Následující příklad ukazuje požadavek, který určuje hlavičku Accept s hodnotou application/vnd.adventure-works.v1+json. Element vnd.adventure-works.v1 webovému serveru říká, že má být vrácena verze 1 prostředku, zatímco element json určuje, že formát těla odpovědi má být JSON:

GET https://adventure-works.com/customers/3 HTTP/1.1
Accept: application/vnd.adventure-works.v1+json

Kód, který zpracovává požadavek, je zodpovědný za zpracování hlavičky Accept a její co možná největší dodržení (klientská aplikace může v hlavičce Accept určit víc formátů, v takovém případě může webový server zvolit pro tělo odpovědi nejvhodnější formát). Webový server potvrdí formát dat v těle odpovědi pomocí hlavičku Content-Type:

HTTP/1.1 200 OK
Content-Type: application/vnd.adventure-works.v1+json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Pokud hlavička Accept neurčuje žádné známé typy médií, webový server by měl generovat zprávu odpovědi HTTP 406 (Nepřijatelné) nebo vrátit zprávu s výchozím typem média.

Tento přístup je pravděpodobně nejčistší z mechanismů správy verzí a přirozeně se propůjčuje HATEOAS, kdy odkazy na prostředky můžou zahrnovat typ MIME souvisejících dat.

Poznámka:

Když vybíráte strategii správy verzí, měli byste také zvážit dopad na výkon, hlavně na ukládání do mezipaměti na webovém serveru. Schémata správy verzí pomocí identifikátoru URI a správy verzí pomocí řetězec dotazu jsou k mezipaměti ohleduplná, protože stejná kombinace identifikátoru URI nebo řetězce dotazu vždycky odkazuje na stejná data.

Mechanismy správy verzí pomocí hlavičky a pomocí typu média obvykle vyžadují další logiku ke kontrole hodnot ve vlastní hlavičce nebo hlavičce Accept. V rozsáhlých prostředích může to, že mnoho klientů používá různé verze webového rozhraní API, způsobit významné množství duplicitních dat v mezipaměti na straně serveru. Tento problém se může akutním, pokud klientská aplikace komunikuje s webovým serverem prostřednictvím proxy serveru, který implementuje ukládání do mezipaměti a který pouze předává požadavky na webový server, pokud aktuálně nemá kopii požadovaných dat v mezipaměti.

Open API Initiative

Iniciativa Open API Initiative byla vytvořena odvětvovým konsorciem, aby došlo ke standardizaci popisů rozhraní REST API mezi dodavateli. V rámci této iniciativy se specifikace Swagger 2.0 přejmenovala na specifikaci OpenAPI (OAS) a začlenila se do Open API Initiative.

Možná budete chtít použít OpenAPI pro vaše webová rozhraní API. Některé body ke zvážení:

  • Specifikace OpenAPI obsahuje sadu závazných pokynů, jak by se měla navrhovat rozhraní REST API. Přináší výhody z hlediska spolupráce, ale vyžaduje větší péči při navrhování rozhraní API tak, aby odpovídalo specifikaci.

  • OpenAPI podporuje přístup, který začíná kontraktem, nikoli implementací. Začít kontraktem znamená, že nejprve navrhnete kontrakt rozhraní API, a teprve pak píšete kód, který kontrakt implementuje.

  • Nástroje jako Swagger dokážou z kontraktů rozhraní API vygenerovat klientské knihovny nebo dokumentaci. Podívejte se například na stránky nápovědy k webovému rozhraní API ASP.NET pomocí Swaggeru.

Další kroky

  • Pokyny k rozhraní MICROSOFT Azure REST API. Podrobná doporučení pro návrh rozhraní REST API v Azure

  • Kontrolní seznam webového rozhraní API Užitečný seznam položek, které se mají vzít v úvahu při navrhování a implementaci webového rozhraní API

  • Open API Initiative. Dokumentace a podrobnosti implementace specifikace OpenAPI