REST API jako rozhraní desktopové aplikace

Winstrom

Jedním z požadavků zákazníků na desktopový ekonomický systém WinStrom 10 bylo otevřené API. Původním zadáním rozhraní byla možnost importovat a exportovat data a provádět další operace. V tomto článku si ukážeme důvody, které nás vedly k implementaci technologií, které využívají čistě internetové servery, jako je Twitter či Google.

Při výběru technologie, která by nám měla umožnit přístup k ekonomickému systému, jsme měli několik možností. Nicméně naše požadavky byly trošku specifické. Potřebovali jsme:

  • umožnit síťový přístup k rozhraní,
  • jeho funkčnost napříč podporovanými operačními systémy (Windows, Linux a Mac OS X),
  • možnost volání z různých programovacích jazyků.

Před realizací jsme dále kladli velký důraz na jednoduchost a intuitivnost. Ideálem bylo pro nás takové rozhraní, které nepotřebuje dokumentaci.

Co používá konkurence

Před samotnou realizací jsem se porozhlédli co nabízí konkurence. V podstatě všechna studovaná řešení provádí výměnu dat vlastním formátem postaveným na XML. Ke komunikaci využívají následující rozhraní:

  • COM/DCOM/Acti­veX/OLE: technologie, které jsou velmi používané ve světě Windows. Jak už jsem ale psal, naše rozhraní musí fungovat i na Linuxu a Mac OS X a navíc má do jednoduchosti daleko (z historických důvodů). Proto jsme je rovnou zavrhli.
  • Pouštění z příkazové řádky: celkem originální postup, kdy připravíme XML a spustíme program, který si data naimportuje. Nevýhodou tohoto řešení je, že neumožňuje síťový přístup, anebo jen velmi omezeně.
  • RMI: protože je naše aplikace napsaná v Javě, zvažovali jsme také použití protokolu RMI. Nicméně toto rozhraní má jednak problémy s průchodem přes NAT (tj. zde nechodí automaticky) a za druhé je použitelné jen z programovacího jazyka Java.
  • WebServices: poměrně silný kandidát, který je dnes hojně využíván napříč programovacími jazyky a platformami. Slabou stránkou tohoto rozhraní je jednoduchost.
  • REST API: zvažujeme-li webové služby a protokol HTTP, logicky musíme dojít i k rozhraní postavené na REST API (Representational State Transfer). To je postavené na webových technologiích (HTTP) a je velmi jednoduché na používání. Pro jeho prozkoumání a také vyvolání stačí uživateli běžný webový prohlížeč. Protokol HTTP umožňuje velký rozsah funkcí. Mimo jiné nabízí autorizaci, cachování, expiraci či vysokou škálovatelnost. Více informací o REST najdete v článku REST: architektura pro webové API.

Po důkladné analýze jsme se nakonec rozhodli pro REST API.

Problémy, které jsme museli překonat

Základem REST je protokol HTTP (i když mohou být i jiné). Proto jsme potřebovali HTTP server. Obvykle se používá webový server Apache, který pak přistupuje k aplikaci (např. z PHP aplikace). Nicméně my jsme tento přístup nemohli použít, protože bychom komplikovali proces instalace a dalšího nastavení. A tak nám zbyla jen jedna možnost: desktopová aplikace musí sama o sobě podporovat HTTP server.

Pro tyto účely jsme zvolili implementaci HTTP serveru Jetty. Jetty je velmi malý a rychlý HTTP server, který startuje velmi krátce, a tudíž nezpomaluje spuštění aplikace. Měli bychom upřesnit, že naše aplikace používá architekturu klient/server/SQL server, a proto je obsluha HTTP součástí našeho serveru. Tato kombinace je používána i jako jednouživatelská instalace, a tak jsme nechtěli nijak prodloužit start a paměťové zatížení.

Jak už bylo zmíněno, jako základ komunikace jsme použili XML. Pro zjednodušení práce s rozhraním REST API z JavaScriptu jsme přidali podporu formátu JSON. V našem případě provádíme automatickou konverzi z XML na JSON, a proto je struktura obou formátů shodná. Nakonec se ukázalo, že i v jazyce PHP je snazší pracovat s formátem JSON než s XML.

Protože rozhraní podporuje více formátů, využíváme podpory protokolu HTTP pro automatickou domluvu formátu pomocí hlavičky Accept. Když chce uživatel rozhraní soubor ve formátu XML, použije hlavičku: Accept: application/xml. Narazili jsme ovšem na problém: některé prohlížeče (např. ten v mobilech s Androidem) jako první uvádějí, že chtějí XML, a přitom myslí XHTML. Proto jsme museli vytvořit filtr, který pokud najde mezi požadovanými formáty text/html, tak jej zařadí na začátek. Stejně tak pokud prohlížeč pošle jen Accept: */* (tj. přijímám všechny formáty), posíláme implicitně HTML verzi. Pokud tedy chcete XML či JSON, je nejlepší uvést do hlavičky Accept jen tento formát. Protože dnešní prohlížeče uživateli neumožňují snadno ovlivnit tuto hlavičku, museli jsme navíc přidat podporu pro domluvu formátu dle přípony. Uživatel může použít URL GET /c/moje_firma/faktura-vydana.json, získat výstup ve formátu JSON a přitom nepotřebuje další nástroje.

Struktura URL

I když už aplikace podporuje protokol HTTP, jsme stále na začátku. Nyní musíme navrhnout strukturu URL, kterou bude naše aplikace používat. My jsme potřebovali standardní CRUD (Create/Read/Up­date/Delete) strukturu:

  • Výpis objektů: GET /c/moje_firma/faktura-vydana
  • Přečtení objektu: GET /c/moje_firma/faktura-vydana/1
  • Založení objektu: PUT /c/moje_firma/faktura-vydana
  • Změna objektu: PUT /c/moje_firma/faktura-vydana/1

U výpisu objektů potřebujeme zpracovávat další případy:

  • stránkování: ?limit=10&start=10
  • informace o počtu objektů kvůli stránkování: ?add-row-count=true přidá do generovaného dokumentu informace o počtu objektů
  • řazení: ?order=nazev případně vzestupně ( ?order=nazev@A) a sestupně ( ?order=nazev@D)
  • filtrace: vytvořili jsme jednoduchý filtrační jazyk s možností and/or, závorkování a podmínek: (datSplat < now() and uzivatel = me())

Winstrom

Výstupní formáty

Kromě již zmíněných XML, JSON a HTML jsme přidali podporu pro další formáty. Prioritou pro nás byl export do formátu PDF či do e-faktury ISDOC. Následně, kvůli integraci s dalšími aplikacemi, vznikl export do e-vizitky vCard a kalendáře iCal. Díky tomu si uživatelé mohou ve svém kalendáři zobrazit splatnosti neuhrazených faktur. Protože se jedná jen o výstupní formát, lze na něj aplikovat funkce pro výpis objektů jako je filtrace či řazení.

PDF

Identifikace objektů

Každému objektu (faktuře, kontaktu atd.) je vždy systémem přidělen jednoznačný číselný identifikátor. Ten je používán jako součást URL. Abychom si zjednodušili práci, používáme jako identifikátory i další symboly:

  • vnitřní ID, přidělované ekonomickým systémem: /c/moje_firma/adresar/1
  • kód firmy: /c/moje_firma/adresar/code:FIRMA
  • DIČ firmy: /c/moje_firma/adresar/vatid:CZ28019920
  • čárový kód EAN: /c/moje_firma/adresar/ean:8594040311025

Tyto identifikátory lze použít jako součást URL i jako součást importovaného XML (např. odkaz na firmu v adresáři nebo zboží v katalogu).

Přidali jsme také podporu tzv. externích ID, které umožňuje uložit identifikátor dalšího systému přímo k objektu v ekonomickém systému. Mapování mezi systémy tak může být přímo ve WinStromu. Externí ID je ve formě: ext:FAKTURACNI1:123, kde FAKTURACNI1 je identifikátor dalšího systému.

Aby nedocházelo ke vzniku duplicitního obsahu (stejný objekt pod různými typy identifikátorů), je při použití jiného než vnitřního ID použito HTTP přesměrování (302 Permanently Moved).

Aktualizace a validace

Programátorské rozhraní musí umožňovat nejen vytvoření objektu, ale i jeho změnu, nebo jen změnu jeho části (atributu). Pokud je uveden atribut (např. název firmy), dojde k jeho přepsání. Pokud není, ke změně nedojde a zůstane nastavena původní hodnota. Pokud chceme hodnotu vynulovat, uvedeme prázdný atribut.

To samé jsme aplikovali i na podobjekty (např. položky faktury). Podobjekty lze měnit, přidávat a mazat.

Neméně důležitou kapitolou je validace. Když přes rozhraní odešleme data do systému, chceme získat seznam validačních chyb nebo varování. Protože se některé položky umí automaticky vypočítat, může uživatel rozhraní chtít odeslat data na server, nechat zvalidovat a přijmout zpátky přepočtená. Pro tyto účely slouží testovací režim. Ten se zapíná parametrem ?dry-run=true. V takovém případě se data neukládají, ale jsou vrácena v takovém stavu, v jakém by byla uložena.

Zvyklosti v REST API

Při vývoji rozhraní jsme zkoušeli komunikovat s aplikacemi psanými v různých frameworcích a jazycích. Při těchto pokusech jsem naráželi na to, že REST je architektonický styl a nedefinuje žádný typy použití, jako je stránkování či filtrace. Z těchto důvodů každý nástroj očekává jiné parametry či data v jiném formátu.

Ukázkou může být typický požadavek z naší aplikace:

GET /c/moje_firma/faktura-vydana/(dic='CZ123456')
<winstrom version="1.0">
    <faktura-vydana>
        <id>1</id>
    </faktura-vydana>
</winstrom>

A v Ruby On Rails:

GET /c/moje_firma/faktury-vydane?dic=CZ123456
<faktury-vydane>
    <faktura-vydana>
        <id>1</id>
    </faktura-vydana>
</faktury-vydane>

Na předchozím příkladu je vidět, že ta samá věc může mít různé reprezentace. Problém jsme vyřešili tak, že se vždy snažíme podporovat více variant (např. různé URL, různé způsoby řazení, …). V těch případech, kdy to nejde, umožňujeme přepnutí režimu pomocí  ?mode=ruby.

Automatická dokumentace

Jedním z požadavků, které jsem na začátku uvedl, byla i jednoduchost použití pro vývojáře. Snažili jsme se vše vytvořit tak, aby základní vývojářská dokumentace byla vždy součástí produktu. Proto do všech generovaných XML dokumentů automaticky doplňujeme i komentáře o názvu položky, jejím typu, délce a seznamu povolených hodnot. Abychom generované dokumenty zbytečně nenafukovali, doplňujeme tuto informaci jen u prvního objektu. Pokud uživatel rozhraní dostane vygenerovaný XML, ihned vidí, co která položka znamená.

V jednoduchosti použití rozhraní jsme zašli ještě dále, a tak jsme do aplikace přidali možnost přepnout si stránky do vývojářského režimu. Díky tomu místo výpisu faktur dostaneme informaci o tom, co daná stránka zobrazuje, jaké je zapnuté stránkování a zda případně jaký je aplikován filtr. Je také možné si přepnout na seznam položek, kde je o každé položce zobrazeno mnohem více informací. Tyto informace je opět možné zobrazit ve formátu HTML, XML a JSON.

Budoucnost

Už jsme se zmínili o možnosti exportovat data do formátu iCal. Možností, které toto rozhraní přináší, je ovšem mnohem více. Mohli bychom si na iGoogle zobrazovat prodeje za poslední měsíc, v mapách zobrazovat prodeje pro jednotlivé oblasti a nebo jen seznam zákazníků v okolí nějakého místa kam se právě chystáme.

Všechny tyto možnosti nyní ale naráží na velký problém. Tím je autorizace přístupu. Standardně je používána HTTP autorizace (tedy jméno a heslo). Nicméně pokud bych chtěl Google kalendáři umožnit přístup k mým datům, narazím na problémy. I kdyby Google uměl přistupovat k zabezpečeným datům, otázka zní, zda bychom mu to vůbec chtěli umožnit. Kvůli takovéto drobnosti bychom mu vlastně umožnili kompletní přístup k celému systému (používal by náš účet). Když nad tímto problémem zapřemýšlíte, hned vás napadne: ale tohle už jsem přeci řešil u Facebooku a Twitteru. Zde také jednotlivým aplikacím povoluji přístup k datům a přitom jim nemusím předávat jméno a heslo. Proto bychom chtěli implementovat zabezpečení pomocí OAuth.

Do budoucna se chystáme dále rozšířit možnosti rozhraní a podpořit vývojáře, kteří jej budou používat. Chceme vytvořit ukázkové aplikace, které budou demonstrovat možnosti.

Absolvent FAV ZČU, obor Softwarové inženýrství. V současné době je výkonný ředitel společnosti FlexiBee Systems.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 74

Přehled komentářů

Jens.cz Reklama na WinStrom?
Martin Malý Re: Reklama na WinStrom?
asi to jinak nejde Re: Reklama na WinStrom?
--- Re: REST API jako rozhraní desktopové aplikace
Zorg Re: REST API jako rozhraní desktopové aplikace
Martin Malý Re: REST API jako rozhraní desktopové aplikace
q Re: REST API jako rozhraní desktopové aplikace
Martin Malý Re: REST API jako rozhraní desktopové aplikace
Prezdivky nikdy nebyly Re: REST API jako rozhraní desktopové aplikace
Martin Malý Re: REST API jako rozhraní desktopové aplikace
Přezdívka dvojtečka Re: REST API jako rozhraní desktopové aplikace
cgi Re: REST API jako rozhraní desktopové aplikace
davidmach14 Výborný článek z praxe!
alblaho Re: Výborný článek z praxe!
Lael Ophir Re: Výborný článek z praxe!
Martin Trapné PRko
Petr Ferschmann Re: Trapné PRko
Prezdivky nejsou Re: Trapné PRko
Petr Ferschmann Re: Trapné PRko
Přezdívka nikdy povinná nebude.... :) Re: Trapné PRko
Petr Ferschmann Re: Trapné PRko
Tomáš Re: Trapné PRko
Martin Malý Re: Trapné PRko
Přezdívky na rootu Re: Trapné PRko
Martin Malý Re: Trapné PRko
Prezdivky jednoduse povinne nejsou Re: Trapné PRko
Petr Ferschmann Re: Trapné PRko
Petr Ferschmann Re: Trapné PRko
Martin Malý Re: Trapné PRko
Prezdivka je blbost Re: Trapné PRko
Tomas Re: Trapné PRko
Martin Malý Souhrnná odpověď
Ped Re: Souhrnná odpověď
jan.letko Re: Souhrnná odpověď
Jens.cz Re: Souhrnná odpověď
Martin Malý Re: Souhrnná odpověď
jan.letko Re: Souhrnná odpověď
Martin Malý Re: Souhrnná odpověď
Ped Re: Souhrnná odpověď
Martin Malý Re: Souhrnná odpověď
Jaro Re: Souhrnná odpověď
Ivan Nový Re: Souhrnná odpověď
Michal Augustýn pěkné
uf priklad z praxe je v poradku
karmi Diky za realnou case-study a propagaci RESTful rozhrani u nas
Petr Ferschmann Re: Diky za realnou case-study a propagaci RESTful rozhrani u nas
karmi Re: Diky za realnou case-study a propagaci RESTful rozhrani u nas
Petr Ferschmann Re: Diky za realnou case-study a propagaci RESTful rozhrani u nas
backup snaha informatiky byt neco vice
uf Re: snaha informatiky byt neco vice
Kvík Re: snaha informatiky byt neco vice
Petr REST Knihovna a autentizace
karmi Re: REST Knihovna a autentizace
karmi Re: REST Knihovna a autentizace
Petr Ferschmann Re: REST Knihovna a autentizace
Petr Re: REST Knihovna a autentizace
Petr Ferschmann Re: REST Knihovna a autentizace
skrat Lokalnici
Petr Ferschmann Re: Lokalnici
Jirka Hradil Poděkování za článek
uf pekne
uf Re: pekne
uf Re: pekne
Karel Re: pekne
Ped Re: pekne
paranoiq díky
uf Dotaz: jak se resi autentifikace u RESTful ?
uf Re: Dotaz: jak se resi autentifikace u RESTful ?
Jan Pejša HTTP autorizace a CSRF
xurfa WebDAV
pk Re: WebDAV
michalekz Sem s takovými "PR články"
Kamil Zabezpeceni
Uf Re: Zabezpeceni
Zdroj: https://www.zdrojak.cz/?p=3079