headerbanner

JSONP jednoduše až po AJAX v jQuery

Autor: Petr Liška

JSONP je způsob asynchronní komunikace javascriptu na stránce v jedné doméně s jinou doménou. Názvy jako "callback funkce" nebo "služby vzdáleného přístupu" zbytečně komplikují pochopení principu JSONP. Ve skutečnosti je to velmi jednoduché.

Javascript z bezpečnostních důvodů umí přímo načítat data jen z domény, ze které pochází zobrazovaná stránka. Celé skripty však může stránka z načítat i z jiné domény. Toho JSONP využívá. Uplatňuje se například při čtení dat ze služeb Google.

Když můžete načíst skript odjinud, můžete tedy načíst jak funkce, tak data v jakémkoli formátu, třeba XML i HTML. JSONP načítá data v JSON formátu, který je méně "ukecaný" než XML a jednodušeji se s ním zachází. Základ je velmi prostý:

 
document.write('<script src="http://nejakadomena.cz/script.php"><script>');
 

Řetězec v argumentu funkce document.write() je tím samým řetězcem, který se běžně staticky vkládá do sekce HEAD nebo na konec tagu BODY (jediný rozdíl je, že statický javascript má obvykle koncovku .js, ale to není podstatné). Když ho vložíte dynamicky, tedy javascriptem, dostanete princip JSONP. Vyzkoušet to můžete třeba zde. Když pak document.write() zavoláte až na základě nějaké události - třeba kliknutí na tlačítko, je princip JSONP úplný.

Volat celý výkonný javascript odněkud "zdaleka" není ani trochu bezpečné. Musíte důvěřovat majiteli jiné adresy, že vám po čase správně fungujícího skriptu nepodstrčí třeba skript z příkladu, vypisující znepříjemňující varování. Vtip JSONP je v tom, že poskytuje pouze data a "obalí" je funkcí, kterou sami předem definujeme a máme tak plnou kontrolu. Nevíme sice jistě, zda nám budou poskytnuta očekávaná data, ale můžeme je ošetřit.

Tedy v principu - na svém webu definujeme funkci:

foo(obj){něco dělej s obj;}

Pak vyšleme dotaz na cizí server, obsahující foo(), tedy bez argumentu. Tento server zpátky pošle úplný javascript spočívající ve volání foo(obj), tedy naplní argument objektem.

JSONP je vlastně JSON s vycpávkou (JSON Padding) - jsou to data ve formátu JSON, obalená funkcí. Nebo jinak - komunikace JSONP spočívá v "natažení" javaskriptu, který volá funkci s parametrem v datovém formátu JSON:

 
foo({"a":"alfa"});
 

Natahujeme tedy javascript, který se po stažení snaží vykonat funkci foo(). Jestliže ji předem definujeme, pak stažený skript nedělá nic jiného, než že ji spouští s uvedeným parametrem, tedy v našem příkladu s objektem, ve kterém a má hodnotu "alfa".

V principu: V javascriptu na vlastní stránce tedy definujeme funkci s názvem foo() (tzv. callback funkci), která umí zpracovat data ve formátu JSON. Následně navěsíme na nějakou událost funkci document.write(), která do dokumentu zapíše element SCRIPT s atributem SRC, ukazujícím na stránku, poskytující odpověď ve formátu JSONP.

Místo document.write() přepisující dokument je jistě praktičtější použít třeba:

 
var script = document.createElement('script');
script.src = 'adresa souboru javascriptu mimo naši doménu';
document.getElementsByTagName('head')[0].appendChild(script);
 

... ale tady nejde o praktický příklad, pouze o vysvětlení, jakým mechanismem JSONP pracuje.

Ještě jednou tedy - JSONP si můžeme představit jako stažení souboru javascriptu s definovaným formátem. Tento javascript je jednoduchým voláním nějaké funkce předem definované na naší stránce.

Výhodou řešení je občerstvení dat bez toho, že bychom museli generovat celou stránku znovu - jedná se o asynchronní komunikaci AJAX (respektive o modifikaci této komunikace obvykle realizované pomocí objektu XMLHttpRequest). Protože v různých prohlížečích používá AJAX různé funkce, je jednodušší nechat zajištění kompatibility na frameworku, například na jQuery.

V jQuery se zcela schová princip JSONP a dokonce ani nemusíme definovat callback funkci:

 
 
var url = 'http://neco.cz/getjson.php'; //adresa poskytující výstup v JSONP
$.ajax({
    type: 'GET', //JSONP funguje pouze s GET požadavkem, nikoli POST
    url: url,
    contentType: "application/x-www-form-urlencoded; charset=UTF-8", //jaká data posíláme stránce getjson.php
    dataType: 'jsonp', //klíčové nastavení pro JSONP - jaká data očekáváme od getjson.php
    success: function(jsondata) {
       //...zpracování JSON dat
    },
    error: function(e) {
       //...
    }
});
 
 

Parametr dataType je zásadní. Popisuje typ očekávaných dat a při jeho uvedení je proměnná url automaticky doplněna o parametr callback dejme tomu na http://neco.cz/getjson.php?callback=nahodnegenerovanyretezec&infoprophp=neco. PHP skript getjson.php na serveru pak vykoná následující:

  • přečte parametr callback (z glob. proměnné $_GET['callback'] ),
  • vyhodnotí ostatní parametry (zde prezentované infoprophp=neco) a připraví podle nich obsah výstupu,
  • zakóduje předávaná data do JSON formátu {"hod1":"456","hod2":"trebatext","hod3":"...."},
  • předchozí data obalí funkcí se jménem nalezeným v parametru callback, tedy do formátu nahodnegenerovanyretezec({"hod1":"456","hod2":"trebatext"})
  • pošle data na svůj výstup.

Data pak převezme framework v našem počítači a spustí funkci definovanou v parametru success, této funkci předá jako parametr již samotná JSON data.

Funkce z parametru callback se zdánlivě vytratila. Vtip je v onom automatickém generování jejího náhodného názvu. PHP skript musí vrátit data zabalená právě do této funkce, framework si její název po náhodném vygenerování pamatuje a použije ho tak, aby to ve výsledku vypadalo, že se volá anonymní funkce z parametru success.

Funkce $.ajax v jQuery je podstatně komplexnější, než napovídá ukázka. Umožňuje například definovat vlastní callback funkci, nemusíme to nechávat na automatickém generování atd., ale to už je mimo rozsah tohoto textu.

Na závěr tedy ještě jednou - JSONP je vložením javascriptu z jiné domény do naší stránky. Vložení je dynamické - ovládané javascriptem. Vkládaný javascript musí být voláním funkce, která se na naší stránce již nachází. Data v parametru této funkce závisí na našich požadavcích, definovaných v parametrech url, ze které javascript stahujeme. Ve výsledku se celý proces tváří podobně, jako kdybychom získávali holá data v JSON formátu ze stejné domény, z jaké pochází naše stránka.