JSON Schema v praxi - Zdroják

Transkript

JSON Schema v praxi - Zdroják
JSON Schema v praxi - Zdroják
Stránka 1
JSON Schema v praxi
Články - Honza Javorek (http://www.zdrojak.cz/autori/honza-javorek/) - Různé (http://www.zdrojak.cz/ruzne/) - 14.5.2014
Historie je plná příběhů o tom, jak zaostalé kmeny dobudou vyspělou civilizaci, její výdobytky zavrhnou a v zemi
následně zavládne několik staletí postupného znovuvynalézání. Totéž dnes sledujeme v přímém přenosu – JSON
byl velkým protestem proti XML, jenže jak dospívá jeho vlastní ekosystém, přichází se na to, že některé staré
nápady možná až tak špatné nebyly. Jedním z nich je koncept schématu – způsobu, jak zapsat, jakou strukturu a
typy by měla určitá data mít. V dnešním článku se dovíte, jak můžete snadno JSON Schema využít ve své aplikaci
a usnadnit si tak práci s validací dat.
Jak bylo naznačeno v úvodu článku, schéma popisuje strukturu a typy, jaké by měla data mít. XML i JSON jsou oba velice obecné formáty, takže je v nich
možné zapsat téměř cokoliv. Chceme-li si data jen pro vlastní potřebu uložit a zase je načíst, schéma tolik neoceníme. Pokud bychom je ale rádi s někým
sdíleli nebo je od někoho přijímali, bylo by už dobré druhé straně sdělit, jak mají vlastně vypadat – kde má být jaké políčko a jestli v něm má být číslo, nebo
třeba seznam řetězců. To lze učinit buď psanou dokumentací, nebo strojovým popisem – schématem.
Výhodami strojového popisu je jednoznačnost a možnost vůči němu snadno a automaticky data validovat. Nejlepší pro protistranu samozřejmě je, když jí
nabídneme obě varianty – jak popisnou dokumentaci, tak schéma.
Příklad ze života – užití schéma pro export z e-shopu
Představte si, že programujete e-shop a chcete v něm mít XML export pro Heureku. Najdete si dokumentaci (http://sluzby.heureka.cz/napoveda/xml-feed/),
která vám dává představu o podobě dat, jaké máte posílat, a dáte se do programování. Jenže lidský popis není vše – někdy jsou v něm nejasné formulace a
rozhodně se vůči němu dost špatně automaticky validuje. Často se tak stane, že máte ve svém systému chybu, ta vygeneruje špatné XML, na Heurece to
udělá neplechu a vám pak utíkají milionové tržby, ani o tom nevíte. Kdyby Heureka publikovala přesné schéma (http://www.kosek.cz/xml/schema/) pro svůj
XML feed, mohl by si každý automaticky zkontrolovat, zda produkuje správně strukturovaná data – tedy zda dobře pochopil dokumentaci a neudělal žádnou
chybu při implementaci. Stejně tak i Heureka by mohla podle téhož schéma snadno validovat příchozí XML a upozorňovat e-shopy na chyby. (Chybějící schéma
se dnes dohání externími nástroji (http://www.mergado.cz/audit-xml).)
JSON Schema
Ten samý problém lze nyní elegantně řešit i pro JSON díky JSON Schema (http://json-schema.org/). Pokud vás hned z hlavy nenapadají situace, kde se to
může hodit, zkusím jich pár nabídnout:
Můžete schématem popsat své API, jako to udělalo Heroku (https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api). Budete
systematicky validovat data, která do vašeho API přicházejí. Používáte Apiary (http://apiary.io/)? I tam je podpora pro JSON Schema (http://
support.apiary.io/knowledgebase/articles/147279-json-schema-validation)!
Počkáte si, až bude v HTML možné (https://darobin.github.io/formic/specs/json/) posílat formuláře s enctype="application/json" a pak si je budete
přes schéma validovat. K jedné definici pravidel dostanete zdarma dvě implementace – podle téhož schématu můžete validovat jak na serveru, tak i v
JavaScriptu na klientovi.
Máte aplikaci s konfiguračním souborem v JSONu a chcete dát uživateli příjemnější chybovou hlášku, pokud se někde splete, než jen Config file invalid,
see docs.
Nebudu vás v tomto článku zásobit příklady, jak má JSON Schema vypadat (http://json-schema.org/example1.html), ani opisovat pěknou příručku, která
podrobně celou technologii vysvětluje (https://spacetelescope.github.io/understanding-json-schema/). Chtěl bych vám prakticky ukázat, jak můžete jednoduše
JSON Schema včlenit do své aplikace.
http://www.zdrojak.cz/clanky/json-schema-praxi/
13.3.2015 11:36:41
JSON Schema v praxi - Zdroják
Stránka 2
Knihovny pro validaci
Kontrolu podle schéma samozřejmě nebudeme dělat ručně – prvním krokem tedy bude nalezení vhodné knihovny pro náš jazyk. Moje příklady budou v
Pythonu, takže se podívám na seznam software pro JSON Schema (http://json-schema.org/implementations.html) a vyberu si tu nejpoužívanější, jsonschema
(https://github.com/Julian/jsonschema). Pojďme se v interaktivním Pythonu podívat, jak práce s knihovnou vypadá:
$ pip install jsonschema
$ python
>>> schema = {
...
'type' : 'object',
...
'properties' : {
...
'age' : {'type' : 'number'},
...
'name' : {'type' : 'string'},
...
},
... }
Takto jsme si připravili jednoduché schéma. Není to samozřejmě přímo JSON, ale jeho reprezentace v Pythonu pomocí datové struktury dict . Nyní zkusme
oproti tomuto schéma validovat nějaká data:
>>> from jsonschema import validate
>>> validate({'name' : 'Honza', 'age' : 42}, schema)
Výstupem posledního řádku nebude nic. Což je dobře, protože funkce validate nic neprodukuje, pokud ji nakrmíme platným vstupem. Zkusme ji trošku
pozlobit:
>>> validate({'name' : 'Honza', 'age' : 'Life, the Universe and Everything'}, schema)
Traceback (most recent call last):
...
ValidationError: 'Life, the Universe and Everything' is not of type 'number'
Failed validating 'type' in schema['properties']['age']:
{'type': 'number'}
On instance['age']:
'Life, the Universe and Everything'
Vstup neprošel – přesně jak jsme očekávali. validate vyhodilo výjimku, z níž můžeme dokonce zjistit nejrůznější detaily o tom, kde a co přesně nesedí.
Takovéto jednoduché použití najdete samozřejmě i na první stránce v dokumentaci. My si dále na vlastním příkladě ukážeme, jak lze schémata načítat ze
souboru, jak je vnořovat do sebe a proč je důležité, aby se naše knihovna nezastavila u první chyby.
Praktická ukázka – validace vstupních dat ve vašem API
Nejdříve si připravíme kousek našeho fiktivního API. K tomu budeme potřebovat webový framework – vezměme tedy něco hodně jednoduchého, řekněme
Flask (http://flask.pocoo.org/). Přibereme si k němu i nějaký testovací nástroj, ať můžeme rychle odzkoušet, zda nám vše funguje:
$ pip install flask pytest
V souboru web.py načrtneme jednoduché view, které bude odpovídat na POST požadavky a přijímat na cestě /users data k založení nového uživatele:
http://www.zdrojak.cz/clanky/json-schema-praxi/
13.3.2015 11:36:41
JSON Schema v praxi - Zdroják
Stránka 3
from uuid import uuid4 as uuid
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/users', methods=['POST'])
def users():
data = request.get_json()
data['id'] = uuid()
# predstirame ukladani do databaze
return jsonify(data), 201
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
Následně si do souboru tests.py napíšeme test, který odesílá data na adresu /users a snaží se nového uživatele založit:
import json, pytest
@pytest.fixture
def test_client():
from web import app
app.config['TESTING'] = True
return app.test_client()
def test_send_data(test_client):
data_in = {'name': 'Honza', 'age': 42}
response = test_client.post('/users', data=json.dumps(data_in),
content_type='application/json')
assert response.status_code == 201
data_out = json.loads(response.data)
assert data_out['id']
assert data_out['name'] == data_in['name']
assert data_out['age'] == data_in['age']
Takovýto test projde – spustíme-li v konzoli py.test tests.py , bude na nás vše zeleně svítit. Pokud si aplikaci nastartujete přes python web.py , najdete ji
v prohlížeči na http://localhost:5000/users , ale nic moc kloudného vám tam asi neodpoví, protože vůbec neobsluhujeme metodu GET. Lepší to bude
třeba s curl :
$ curl -X POST -H 'Content-Type: application/json' -d '{"name":"Honza","age":42}' 'http://localhost:5000/users'
{
"id": "79e015d7-a3f6-4619-867a-9c8fcf6f0d56",
"age": 42,
"name": "Honza"
}
Kód k této fázi naleznete zde (https://github.com/honzajavorek/zdrojak-json-schema/commit/51f486bc8dded35e8b786064d9eb6b5386d1675a) (odkaz přímo na
commit).
Validujeme!
Kostru máme hotovou. Nyní zajistíme, aby nám do aplikace neproniklo nic, co neodpovídá naším požadavkům. Nejdříve si do souboru user.json sepíšeme
jednoduché schéma:
http://www.zdrojak.cz/clanky/json-schema-praxi/
13.3.2015 11:36:41
JSON Schema v praxi - Zdroják
Stránka 4
{
"$schema": "http://json-schema.org/schema#",
"title": "User",
"type": "object",
"properties": {
"age": {"type": "integer"},
"name": {"type": "string"}
},
"required": ["name"],
"additionalProperties": false
}
Jak vidíme, data reprezentující uživatele mají mít pole age pro věk, jejíž hodnotou bude číslo, a pole name pro jméno, jehož hodnotou má být řetězec. Jméno
bude přitom povinné a nedovolujeme nastavit žádná další pole. Nyní začneme připravovat validaci pro naše view:
import json
from jsonschema import Draft4Validator as Validator
def validate(schema_filename, data):
with open(schema_filename) as f:
schema = json.load(f)
# cteme JSON Schema primo ze souboru
Validator.check_schema(schema)
# zkontroluje schema nebo vyhodi vyjimku
validator = Validator(schema)
return validator.iter_errors(data)
# vraci chyby jednu po druhe
Nepoužili jsme funkci validate ze začátku článku, ale píšeme si vlastní, na míru. Dovolí nám to později lépe kontrolovat proces validace. Vybíráme si
specifikaci JSON Schema, s níž chceme pracovat – v tomto případě Draft4 (http://tools.ietf.org/html/draft-zyp-json-schema-04). Nejdříve čteme schéma ze
souboru, parsujeme jej a kontrolujeme, zda je ono samo vůbec v pořádku. Následně podle něj validujeme data a vracíme iterátor s jednotlivými chybami. Díky
tomu, že se naše knihovna při použití iter_errors nezastaví na první chybě, budeme moci uživateli našeho API sdělit všechny nedostatky jím zaslaných dat
najednou. Podívejme se, jak bude vypadat samotné view a testy:
import os
@app.route('/users', methods=['POST'])
def users():
data = request.get_json()
schema_filename = os.path.join(app.root_path, 'user.json')
errors = [error.message for error in validate(schema_filename, data)]
if errors:
return jsonify(errors=errors), 400
data['id'] = uuid()
# predstirame ukladani do databaze
return jsonify(data), 201
def test_invalid_data(test_client):
data_in = {'id': 2, 'name': 'Honza', 'age': 'Life, the Universe and Everything'}
response = test_client.post('/users', data=json.dumps(data_in),
content_type='application/json')
assert response.status_code == 400
data_out = json.loads(response.data)
assert len(data_out['errors']) == 2
http://www.zdrojak.cz/clanky/json-schema-praxi/
13.3.2015 11:36:41
JSON Schema v praxi - Zdroják
Stránka 5
Jedna poučka praví, že HTTP kódy 5xx indikují selhání na straně tvůrce serveru, zatímco 4xx mají hlásit problém na straně uživatele. Budeme se jí tedy držet a
vracíme chybovou odpověď s HTTP kódem 400. Jestliže selže něco jiného, tak spoléháme na skutečnost, že Flask umí z nezachycených výjimek automaticky
vytvořit „pětistovku“ (byť v tomto základním nastavení se bude jednat o HTML stránku a ne o JSON – úprava tohoto chování je ovšem nad rámec ukázky).
Zkusíme-li teď poslat stejná data s curl , odpoví nám server takto:
$ curl -X POST -H "Content-Type: application/json" -d '{"id": 2,"name":"Honza","age":"Life, the Universe and Everything"}' 'http://
localhost:5000/users'
{
"errors": [
"Additional properties are not allowed (u'id' was unexpected)",
"u'Life, the Universe and Everything' is not of type u'integer'"
]
}
Jak vidíme, „zadarmo“ jsme získali mocný validační mechanismus a k němu navíc docela ucházející výchozí chybové hlášky, z nichž si dokáže uživatel našeho
API snadno a rychle domyslet, co dělá špatně.
Kdyby to viděl Horst Fuchs (https://cs.wikipedia.org/wiki/Horst_Fuchs), přihodil by na stůl ještě skutečnost, že nyní máme validaci založenou na jednoduchém
textovém souboru (vhodné např. k verzování), jehož obsah je v nezávislém, standardním formátu (řádka knihoven pro různé jazyky). Můžeme tak bez problémů
tuto „specifikaci“ pro data přijímaná naším API sdílet – a když na to přijde, třeba rovnou na adrese /users/schema . Snadno lze na takové schéma potom
odkázat z dokumentace, nebo přímo z odpovědí v API, např. přes hlavičky (http://tools.ietf.org/html/rfc5988).
Kód k této fázi naleznete zde (https://github.com/honzajavorek/zdrojak-json-schema/commit/ccfc4fd765d471dfaacd44d6070ca4987acda334) (odkaz přímo na
commit).
Vnořená schémata
Představte si, že chceme k uživateli ukládat adresu. Mohli bychom ji samozřejmě snadno dopsat do našeho schéma v user.json jako vnořený objekt, ale
brzy bychom narazili na to, že adresu ukládáme např. i u firem a musíme její definici udržovat na více místech zároveň. Zkusíme si tedy ukázat, jak bychom
mohli ukládat adresu do zvláštního schéma a z popisu uživatele na ni jen odkázat. Nejdříve připravíme schémata:
{
"$schema": "http://json-schema.org/schema#",
"title": "User",
"type": "object",
"properties": {
"age": {"type": "integer"},
"name": {"type": "string"},
"address": {"$ref": "address.json#"}
},
"required": ["name"],
"additionalProperties": false
}
Jak je vidět, odkazujeme na soubor address.json . Pojďme jej vytvořit:
http://www.zdrojak.cz/clanky/json-schema-praxi/
13.3.2015 11:36:41
JSON Schema v praxi - Zdroják
Stránka 6
{
"$schema": "http://json-schema.org/schema#",
"title": "Address",
"type": "object",
"properties": {
"street": {"type": "string"},
"number": {"type": "integer"},
"city": {"type": "string"},
"country": {"type": "string"},
"zip_code": {"type": "integer"}
},
"required": ["country", "city"],
"additionalProperties": false
}
Aby knihovna jsonschema při validaci dokázala odkazovaný soubor najít, musíme rozšířit naši funkci validate a přidat do ní tzv. RefResolver , jenž
nasměrujeme do správné složky:
from jsonschema import RefResolver
def validate(schema_filename, data):
with open(schema_filename) as f:
schema = json.load(f)
# cteme JSON Schema primo ze souboru
Validator.check_schema(schema)
# zkontroluje schema nebo vyhodi vyjimku
base_uri = 'file://' + os.path.dirname(schema_filename) + '/'
resolver = RefResolver(base_uri, schema)
validator = Validator(schema, resolver=resolver)
return validator.iter_errors(data)
# vraci chyby jednu po druhe
A to je vše. Nyní nám zbývá už jen validaci adres odzkoušet v testech:
def test_send_address(test_client):
data_in = {'name': 'Honza',
'address': {'country': 'Czech Republic', 'city': 'Krno'}}
response = test_client.post('/users', data=json.dumps(data_in),
content_type='application/json')
assert response.status_code == 201
data_out = json.loads(response.data)
assert data_out['id']
assert data_out['name'] == data_in['name']
assert data_out['address'] == data_in['address']
def test_invalid_address(test_client):
data_in = {'name': 'Honza',
'address': {'city': 'Krno', 'number': '11', 'mayor': 'Rumun'}}
response = test_client.post('/users', data=json.dumps(data_in),
content_type='application/json')
assert response.status_code == 400
data_out = json.loads(response.data)
assert len(data_out['errors']) == 3
http://www.zdrojak.cz/clanky/json-schema-praxi/
13.3.2015 11:36:41
JSON Schema v praxi - Zdroják
Stránka 7
Hotovo. Finální kód naší malé aplikace je kompletně k dispozici v repozitáři na GitHubu (https://github.com/honzajavorek/zdrojak-json-schema). Celá validace
se pak na základě dalších vylepšení dá slušně automatizovat – do de facto deklarativní roviny ji přesunulo rozšíření (https://github.com/mattupstate/flaskjsonschema#flask-jsonschema)Flask-JsonSchema (https://github.com/mattupstate/flask-jsonschema):
@app.route('/users', methods=['POST'])
@jsonschema.validate('user', 'create')
def users():
...
Závěrem
Cílem článku nebylo vytvořit dokonalou a neprůstřelnou aplikaci nebo hlásat jediný správný způsob, jak něco dělat. Také Python byl vybrán spíše pro názornost
– stejný příklad byste mohli sestrojit v Javě nebo JavaScriptu.
Mým záměrem bylo představit schéma jako obecnou technologii, která je u nás bohužel podužívaná a přitom by mohla vyřešit mnoho zbytečných problémů na
obou stranách „API barikády“. Chtěl jsem prakticky ukázat, že JSON Schema není žádnou pochybnou, nostalgickou iniciativou XML nadšenců, které někdo
donutil přejít na JSON. A v neposlední řadě jsem si přál vyvolat ve vás zájem, jenž by způsobil, že si otevřete návod k JSON Schema (https://
spacetelescope.github.io/understanding-json-schema/) a podíváte se, co všechno dokáže validovat (https://spacetelescope.github.io/understanding-jsonschema/reference/regular_expressions.html) a jaké má pokročilé funkce (https://spacetelescope.github.io/understanding-json-schema/reference/combining.html)
, nebo že se zamyslíte nad tím, jaké možnosti se vám užitím schématu otevírají. Snad se mi to povedlo.
Honza Javorek (http://www.zdrojak.cz/autori/honza-javorek/)
Přispívám v Apiary (http://apiary.io) k tomu, aby se stal API Blueprint (http://apiblueprint.org/) nejlepším formátem pro popis webových API.
Pomáhám v ČR propagovat jazyk Python (http://honzajavorek.cz/blog/proc-python.html). Mám blog Javorové lístky (http://honzajavorek.cz/blog/), kam
píšu, když zrovna nepíšu pro Zdroják.
 (http://honzajavorek.cz)
T (http://www.twitter.com/honzajavorek)
Věděli jste, že nám můžete zasílat zprávičky (http://www.zdrojak.cz/zpravicky-new)? (Jen pro přihlášené.)
Zdroj: http://www.zdrojak.cz/?p=12037
http://www.zdrojak.cz/clanky/json-schema-praxi/
13.3.2015 11:36:41

Podobné dokumenty

Generování syntaktických analyzátoru

Generování syntaktických analyzátoru nedělá to nic zvláštnı́ho, jen využı́vá toho, že lex defaultně čte ze standardnı́ho vstupu. pokud bychom to chtěli vylepšit. . .

Více

Webová aplikace pro sdílení asistentů pro aplikaci

Webová aplikace pro sdílení asistentů pro aplikaci Aplikace DevAssistant, která pomáhá automatizovat opakující se činnost programátorů jinou než programování samotné, umožňuje uživatelům vytvářet vlastní asistenty, tedy něco jako recepty na jednotl...

Více

FV_Příbalová informace

FV_Příbalová informace reakce (PCR). Metoda je založena na amplifikaci cílové sekvence a její detekci pomocí alelově specifického signálu fluorescenčně značených sond. Cílovou sekvencí je jednonukleotidová záměna guaninu...

Více

Návrh privátní IaaS cloudové platformy - Newt on-da-line

Návrh privátní IaaS cloudové platformy - Newt on-da-line Cloudové IaaS platformy nemají jednotný postup instalace (2) (3) (4). Každá instalace je jedinečná, liší se počtem fyzických serverů, možnostmi použité virtualizační platformy a topologií sítě. Pro...

Více

Ruby On Rails = + Ruby + Rails Jan Strnádek

Ruby On Rails = + Ruby + Rails Jan Strnádek Built-in web servers (Webrick, Puma)

Více

Obecné pokyny k řádným zásadám odměňování podle

Obecné pokyny k řádným zásadám odměňování podle č. 1093/2010, na které se tyto obecné pokyny vztahují, by s nimi měly být v souladu a začlenit je do svých postupů (např. pozměněním právního rámce nebo dohledových postupů), včetně případů, kdy js...

Více

Přednáška v PDF.

Přednáška v PDF. Ukázka: jednoduchá aplikace Ukázka: srovnání dalších FW

Více

zde

zde Tímto je spojovaní hotovo. Ověřte, že program lze přeložit a funguje. Nyní proveďte uložení spojené verze do repository pomocí: “SVN Commit“, protože doposud jsou všechny změny uloženy pouze v loká...

Více