Wie wir auf der Seite zur Same-Origin-Policy sehen können, ist es nicht möglich per AJAX auf einen Server zuzugreifen, der nicht mit dem identisch ist, von dem wir zuvor die HTML-Seite geladen haben.
Ausprobieren:
JSONP-Abruf von einem 'fremden' Server:
Antwort:
Im nebenstehenden Experiment versuchen wir nun dasselbe, aber verwenden JSONP anstelle von AJAX. Und in der Tat, diesmal funktioniert es. JSONP ist also in der Lage, die Same-Origin-Policy zu überwinden.
Was ist JSONP?
Exkurs: Was ist eigentlich ...
JSON?
"JavaScript Object Notation" - ist ein Datenformat, das ebenso wie XML gerne verwendet wird, um Daten zwischen Client und Server auszutauschen.
Dabei hat JSON gegenüber XML den Vorteil, dass es zur Verarbeitung nicht aufwendig geparst werden muss, sondern in einem Schwung in ein Datenobjekt oder assoziatives Array umgewandelt werden kann. Damit können sehr einfach auch komplexe Datenstrukturen zwischen Client und Server ausgetauscht werden.
JSONP ist "JSON mit Padding". Wobei JSON ein Datenformat ist, das zur Übertragung durch einen Funktionsaufruf (dem Padding) umschlossen wird. JSONP-Datenzugriffe sind nicht auf den Server beschränkt, von dem die HTML-Seite geladen wurde. Mit JSONP kann über Domänengrenzen hinweg auf alle Server und Webservices zugegriffen werden, die diese Technik beherrschen. Somit ist JSONP eine wichtige Web 2.0 Technologie.
Wie funktioniert JSONP?
Nahezu alle Methoden, aus einer HTML-Datei heraus auf Daten von fremden Servern zuzugreifen, scheitern an der Same-Origin-Policy. Eine Ausnahme wäre das Laden von Bildern ("img"-Tag), die können aus beliebigen Quellen abgerufen werden. Allerdings hilft uns das kaum, da sich Daten nur schwer als Bild übertragen lassen. Eine weitere Möglichkeit, die nicht der SOP unterliegt, ist das Einbinden von Skripten per "script"-Tag. Skripte können im HTML-"head" (und auch im "body") aus beliebigen Quellen im Internet geladen werden. Und im Unterschied zu Bildern lassen sich in Skripten sehr wohl Daten übertragen, indem sie in einen Funktionsaufruf verpackt werden.
Genau das macht JSONP - dabei wird in folgenden Schritten vorgegangen:
- Der Client (also typischerweise JavaScript) muss eine Callback-Funktion bereitstellen, an die später die Daten übergeben werden.
- Der Name der Callback-Funktion muss vom Client an den Server übermittelt werden, zusammen mit anderen Daten, die der Client senden möchte.
- Der Aufruf der Datenübertragung erfolgt am Client durch die Formulierung eines Scriptaufrufs. Dabei wird die URL des Webservices am Server angegeben, ergänzt durch den Namen der Callback-Funktion. Der fertige "script"-Tag muss dann ins DOM (Domain Object Model), also in die Webseite eingeschleust werden. Dieser Vorgang nennt sich "script tag injection" und der löst die Datenübertragung aus.
- Der Webservice auf Serverseite übernimmt die Daten, extrahiert den Namen der Callback-Funktion und verwendet ihn, um bei der Rücksendung die Serverdaten durch einen entsprechenden Funktionsaufruf zu klammern.
- Der Browser erhält die Serverantwort in Form eines Skripts zurück und beginnt sofort, das Skript auszuführen. Da das Skript aus einem Funktionsaufruf besteht, wird die "callback"-Funktion aufgerufen und diese bekommt die Daten als Parameter übergeben.
Anhand unseres Beispiels sieht die Datenübertragung wie folgt aus:
JSONP-Request und -Response:
Anfrage des Clients:
http://webservice.helmutkarger.de/php/jsonp.php?time=1351678219999&callback=callback
Antwort des Servers:
callback('Ihre IP-Adresse lautet:<br/>88.217.251.158<br/>Abgefragt am:<br/> 31.10.2012 um 11:10:11 Uhr (Serverzeit).')
Was hat JSONP mit JSON zu tun?
Eigentlich nichts - mit JSONP können auch andere Datenformate übertragen werden, so wie einfacher Text in unserem Beispiel. Die Verwendung von JSON ist jedoch gebräuchlich und naheliegend, speziell bei komplexeren Datenstrukturen.
Nachteile von JSONP
JSONP ist schnell, universell und browserübergreifend einsetzbar, aber hat doch zwei Nachteile:
- JSONP funktioniert nur mit der HTTP-Methode "GET". Das
liegt in der Natur der Sache, nachdem das Laden von Skripten aus HTML
generell per "GET" erfolgt, ist dies auch für JSONP
verpflichtend. "POST" steht hier (im Unterschied zu
AJAX und CORS)
nicht zur Verfügung. Im allgemeinen sollte das kein großes Problem
darstellen, wenn jedoch große Datenmengen vom Client an den Server
übertragen werden sollen, stellt die begrenzte Länge der URL eine
Einschränkung dar. Um mit allen Browsern arbeiten zu können, sollte
die Länge der URL auf 2048 Zeichen begrenzt werden. Für
die Rückübertragung vom Server zum Client gibt es jedoch keine Längenbegrenzung.
Denkbare Workarounds:- Segmentieren der Uploads in mehrere Pakete, die jeweils unter der Grenze von 2048 Zeichen liegen.
- Senden der großen Datenmenge per AJAX-POST ohne eine Antwort des Servers auswerten zu können und anschließende Abfrage der Serverbestätigung per JSONP.
- Im Unterschied zu AJAX bietet JSONP kaum Möglichkeiten zur Fehlerbehandlung. Die Serverantwort kommt oder sie kommt eben nicht. Denkbar wäre, einen Timer zu setzen und diesen bei Ankunft der Serverantwort zu löschen. Läuft der Timer aus, ist die Datenübertragung gescheitert. Mehr Komfort im Vergleich zur eigenen Programmierung bietet auch das Javascript-Framework jQuery.