JSONP sau „JSON cu umplutură” este o completare la formatul JSON de bază . Oferă o modalitate de a solicita date de la un server situat pe un domeniu diferit, o operațiune care este interzisă în browserele web tipice din cauza politicii de restricție a domeniului .
În iulie 2005, George Jempty a propus posibilitatea de a adăuga JSON înaintea unei declarații de variabile opționale. [1] [2] Propunerea originală JSONP, în care umplutura este o funcție de apel invers, a fost cel mai probabil făcută de Bob Ippolito în decembrie 2005 [3] și este acum utilizată de multe aplicații Web 2.0 , cum ar fi Dojo Toolkit , Google Web Toolkit , [4] și Servicii Web .
Conform politicii de restricție a domeniului , o pagină web situată pe un server example1.comnu poate contacta un alt server decât example2.com. Tehnologia JSONP se bazează pe faptul că politica de securitate a browserului nu interzice utilizarea unui element HTML <script type="text/javascript" src="…"/> pentru a accesa alte servere decât serverul de pe care a fost încărcată pagina. Folosind Open Policy on Elements <script>, unele pagini le folosesc pentru a încărca cod JavaScript care operează pe date JSON generate dinamic din alte surse. Solicitările pentru JSONP nu primesc JSON, ci cod JavaScript arbitrar. Ele sunt procesate de interpretul JavaScript, nu de parserul JSON. Există riscuri serioase de securitate atunci când utilizați JSONP, în majoritatea situațiilor utilizarea CORS este cea mai bună alegere.
Schema modelului poate fi descrisă folosind o solicitare către o anumită adresă URL care returnează date JSON. Un program JavaScript ar putea solicita această adresă URL, de exemplu, prin XMLHttpRequest . Să presupunem că UserId-ul obiectului Foo este 1234. Un browser care solicită o adresă URL http://server2.example.com/Users/1234care trece într-un ID de 1234 va primi un răspuns în următorul format:
{ „Nume” : „Foo” , „Id” : 1234 , „Rang” : 7 }Datele JSON dintr-un răspuns terță parte sunt de obicei generate dinamic, pe baza parametrilor de solicitare trecuți în adresa URL.
Următorul element HTML <script>specifică ca atribut un srclink care returnează JSON:
< script type = "application/javascript" src = "http://server2.example.com/Users/1234" > </ script >La rândul său, browserul va descărca fișierul script, va analiza conținutul acestuia, va interpreta datele brute JSON ca un bloc și va genera o eroare de sintaxă. Chiar dacă datele au fost interpretate ca un literal obiect JavaScript, nu pot fi accesate din JavaScript care rulează în browser, deoarece literalele obiect nu sunt disponibile fără a fi alocate unei variabile.
În modelul JSONP, adresa URL indicată de <script>atributul src al etichetei returnează date JSON împachetate într-un apel de funcție. Într-un astfel de caz, o funcție deja definită în mediul JavaScript poate manipula datele JSON. Umplutura JSONP ar putea arăta astfel:
functionCall ({ „Nume” : „Foo” , „Id” : 1234 , „Rang” : 7 });Apelul funcției este „P” în cuvântul JSONP - „padding” (umplutură, „ indent ”) în jurul JSON pur sau, conform unor surse [5] , - „prefix”. Prin convenție, browserul transmite numele funcției de apel invers ca parametru de cerere numit, de obicei folosind numele jsonpsau callbackîn cererea către server, adică
< script type = "text/javascript" src = "http://server2.example.com/Users/1234?jsonp=parseResponse" > </ script >În acest exemplu, umplerea va fi după cum urmează:
parseResponse ({ „Nume” : „Foo” , „Id” : 1234 , „Rang” : 7 });În timp ce padding (prefixul) este de obicei numele unei funcții de apel invers definit în contextul de execuție din browser. Pe lângă numele funcției, prefixul poate însemna un nume de variabilă, un operator ifsau orice alt operator JavaScript. Răspunsul la o solicitare JSONP (strict vorbind, o solicitare conformă cu modelul JSONP) nu este un obiect JSON și nu este tratat ca atare de către browser. „Umplutura” poate fi orice expresie JavaScript și nu necesită ca JSON să fie în interior. Dar, de obicei, este o bucată de JavaScript care aplică un apel de funcție unor date JSON.
Cu alte cuvinte, o utilizare tipică a JSONP oferă acces pe mai multe domenii la un API JSON existent prin încadrarea umpluturii JSON într-un apel de funcție.
JSONP are sens numai atunci când este utilizat cu un element de script. Pentru fiecare cerere JSONP nouă, browserul trebuie să adauge un element nou <script>sau să folosească unul existent. Prima manipulare, adăugarea unui nou element de script, se face prin manipulare DOM dinamică și este cunoscută sub numele de injectare a elementului de script . Elementul <script>este inserat în DOM HTML, cu URL-ul punctului final JSONP dorit în atributul „src”.
Această injecție dinamică a elementelor de script este de obicei realizată de o bibliotecă de ajutor javascript. jQuery și alte cadre au funcții de ajutor pentru JSONP; există și soluții separate [6] [7] .
Elementul de script inserat dinamic pentru apelurile JSONP arată astfel:
< script type = "text/javascript" src = "http://server2.example.com/Users/1234?jsonp=parseResponse" > </ script >După ce elementul este inserat, browserul îl procesează și efectuează un HTTP GET pe URL-ul src, obținând conținutul. Browserul tratează apoi sarcina utilă returnată ca JavaScript. De obicei, aceasta este execuția unei funcții.
În acest sens, utilizarea JSONP poate fi descrisă ca permițând paginilor browserului să ocolească politica de restricție a domeniului prin inserarea unui element de script.
Includerea etichetelor de script de la alte servere permite serverelor de la distanță să amestece orice conținut în site-ul web . Dacă serverele de la distanță au vulnerabilități care permit amestecarea JavaScript, pagina furnizată de serverul original prezintă un risc crescut.
În prezent, se fac pași pentru a defini un subset mai sigur și mai strict de JSON-P [8] pe care browserele ar putea să-l includă atunci când solicită un script cu un anumit tip MIME, cum ar fi „application/json-p”. Dacă răspunsul nu este analizat ca JSON-P strict, browserul ar putea să arunce o eroare sau pur și simplu să ignore întregul răspuns. Cu toate acestea, în prezent, singurul tip MIME valid pentru JSONP este „application/javascript” [9] .
Gazdele JSONP primitive sunt susceptibile de falsificarea cererilor între site-uri (CSRF sau XSRF) [10] . Deoarece eticheta HTML <script>nu este supusă politicii de restricție a domeniului în implementările reale ale browserului, o pagină rău intenționată poate solicita și primi date JSON care aparțin unui alt site. Acest lucru va permite ca datele JSON să fie procesate în contextul unei pagini rău intenționate, eventual dezvăluind parole sau alte date sensibile dacă utilizatorul este conectat pe alt site.
Acest lucru cauzează probleme numai dacă datele codificate în JSON conțin informații sensibile care nu ar trebui dezvăluite unei terțe părți, iar serverul se bazează pe politica de restricție a domeniului a browserului pentru a bloca transmiterea datelor în cazul unei solicitări greșite. Problema nu există dacă serverul însuși determină oportunitatea cererii, transmițând date doar dacă cererea este validă. Cookie -urile în sine nu reprezintă o modalitate adecvată de a determina legitimitatea unei solicitări. Numai utilizarea cookie-urilor este susceptibilă de falsificare a cererilor pe mai multe site-uri .
JSONPP ( ing. JSON parametrizat cu padding - „JSON parametrizat cu padding”) - dezvoltarea ideii JSONP.
JSONPP include adresa URL sursă, numele funcției care va procesa datele JSON, șirul de evalat după ce datele sunt primite și șirul de evalat când datele sunt terminate:
JSON_call ( SRC , JSONP , JSONPP , ONLOAD );în cele din urmă se întoarce
ans = JSONP ( SRC ) { eval ( JSONPP ( ans )); eval ( ÎNCĂRCARE ); }În general, numărul de parametri nu este important pentru ideea JSONPP în sine. SRC, JSONP, JSONPP (și procesarea lor pe partea serverului și apoi pe partea clientului) este suficient pentru ca acesta să fie JSONPP. Luați în considerare exemplul de lucru cu serviciul S3DB.
funcția s3db_jsonpp_call ( src , next_eval ){ var call = "call_" + Math . aleatoriu (). toString (). înlocuiți ( /\./g , "" ); var headID = document . getElementsByTagName ( "cap" )[ 0 ]; var script = document . createElement ( 'script' ); scenariu . id = apel ; scenariu . tip = 'text/javascript' ; // utilizând json src parametrizat, căptușit = src + "&format=json&jsonp=s3db_jsonpp&jsonpp=" + next_eval + "&onload=remove_element_by_id('" + script . id + "')" ; scenariu . src = src ; headID . appendChild ( script ); // preluați răspunsul } function s3db_jsonpp ( ans , jsonpp ){ eval ( jsonpp ); return ans ; } funcția remove_element_by_id ( id ){ var e = document . getElementById ( id ); e . parentNode . removeChild ( e ); returnează fals ; }În exemplu, funcția s3db_jsonpp_call()creează un element de script în partea principală a DOM al cărui src se potrivește cu apelul JSONPP.
După primirea unui răspuns de la server, acesta va fi apelat s3db_jsonpp() - este transmis în parametrii de apel, așa cum ar trebui să fie conform regulilor JSONP.
Intern , s3db_jsonpp()va funcționa eval(jsonpp), iar valoarea ans va fi returnată.
Apelul eval(onload)duce la execuție remove_element_by_id()cu id-ul scriptului creat în cap și, ca urmare, la ștergerea acestuia, deoarece oricum nu va mai fi folosit, deoarece id-ul din exemplu a fost generat aleatoriu chiar la începutul funcției s3db_jsonpp_call(). Acest apel este în răspunsul serverului.