Dez06
AJAX Cross Domain mit JSONP

Will man aus dem Browser mit JavaScript über AJAX einen REST Service aufrufen, der auf einem anderen Server bzw. in einer anderen Domain liegt, geht das aus Sicherheitsgründen nicht. Wie man doch einen Cross Domain AJAX Call machen kann, möchte ich in diesem Artikel zeigen.

Theorie

Die same-origin policy verhindert, dass ein Script, das von einer Domain A geladen wird, eine Funktion von einer Domain B aufrufen kann. Das hat zur Folge, dass die Domain des Service die gleiche sein muss, wie die Domain der Website. Das Script von Domain A kann also nur einen Service von Domain A aufrufen.
Was die same-origin policy nicht verhindert, ist, dass ein JavaScript von einem anderen Server geladen wird. Es ist also möglich, dass eine Website von der Domain A, ein Script von Domain A und ein anderes Script von einer Domain B lädt. Dies wird häufig praktiziert, wenn z.B. jQeury oder Google Maps direkt von Google geladen werden.
Mit JavaScript ist es auch möglich, JavaScript Code dynamisch hinzuzufügen.

Es ist also nicht möglich über Domaingrenzen einen Service aufzurufen, der z.B. eine JSON Antwort liefert, es ist aber möglich über Domaingrenzen JavaScript zu laden und den JavaScript Code dynamisch zur eigenen Website hinzuzufügen. Also muss die Antwort eines Service nicht als JSON, sondern als JavaScript Code verpackt sein und schon ist der Serviceaufruf über Domaingrenzen möglich. Und genau das macht JSONP.

Die einzige Voraussetzung ist, dass eine Callback Methode zur Verfügung gestellt wird, die vom JavaScript, das der Service liefert, aufgerufen werden kann.

Praxis

Als Beispiel haben wir eine WebSite auf Domain A (http://www.domainA.de) und wollen einen Service von Domain B (http://www.domainB.de) aufrufen, der aus Kontonummer und Bankleitzahl eine IBAN generiert.

Die IBAN wird in einem JSON Objekt dargestellt:

{IBAN: 'DE654560000000123'};

Die Website der Domain A beinhaltet eine Callback Methode, mit der die IBAN angezeigt wird. Diese Methode muss vom JavaScript, das der Service von Domain B liefert, aufgerufen werden:

function showIban(data) {
    alert("Die IBAN lautet: " + data.IBAN);
}

Über folgenden Methodenaufruf wird die IBAN angezeigt.

showIban({IBAN: 'DE654560000000123'});

Genau so muss die JSONP Antwort des Service der Domain B aussehen. Die IBAN ist als Parameter im Methodenaufruf der Callback-Methode enthalten.

Die Website in Domain A ruft also per AJAX den Service von Domain B auf. Der Service liefert als Antwort den JavaScript Code mit dem Methodenaufruf der Callback-Methode. Der gelieferte JavaScript Code wird der Website dynamisch hinzugefügt und ausgeführt. Dadurch wird die Methode showIban aufgerufen und die IBAN ausgegeben.

JSONP mit jQuery

jQuery stellt eine spezielle JSONP Unterstützung zur Verfügung. Es fügt der HTML-Seite dynamisch eine Callback Methode hinzu und gibt den Namen der Methode, als Aufrufparameter beim Serviceaufruf, mit. Die Serviceimplementierung muss diese Callback-Methode im JavaScript der Antwort verwenden.
Der Aufruf des IBAN-Service sieht wie folgt aus:

function getIban() {
	$.ajax({
		url : "http://www.domainB.de/iban",
		type : "GET",
		dataType : "jsonp",
		data : {kto: '123', blz: '456'},
		success : function(data, textStatus, jqXHR) {
			alert("Die IBAN lautet: " + data.IBAN);
		}
	});
}

In diesem Beispiel ist die explizite Callback-Methode showIban nicht notwendig. Die Funktionalität um die IBAN anzuzeigen ist im success Block des AJAX Calls enthalten und die JSONP Callback Methode wird durch jQuery automatisch erzeugt.

Serviceimplementierung

Auf der Seite des Service muss die Antwort als JavaScript verpackt werden. Neben Kontonummer (kto) und Bankleitzahl (blz), wird der Name der Callback-Methode mit dem Parameter callback mitgegeben.
Über eine Prüfung, ob eine Callback-Methode beim Serviceaufruf mitgegeben wurde, kann dynamisch darauf reagiert werden, ob die Antwort als JSON oder als JSONP geliefert werden soll. Somit kann der Service JSON Calls aus der eigenen Domain und JSONP Calls aus anderen Domains unterstützen.

Folgender Code zeigt die Serviceimplementierung in Java. Die Methode zur Berechnung der IBAN und zur Konvertierung in ein JSON-Objekt sind nicht enthalten:

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String blz = request.getParameter("blz");
		String kto = request.getParameter("kto");
		String callback = request.getParameter("callback");

		if (callback == null || callback.length() == 0) {
			response.getWriter().write(getIbanAsJson(blz, kto));
		} else {
			response.getWriter().write(callback + "('" + getIbanAsJson(blz, kto) + "');");
		}
	}

Wird keine Callback-Methode im Service Aufruf mitgegeben, wird die IBAN als JSON Obekt zurück gegeben (Zeile 7). Ist eine Callback-Methode dabei, wird das JSON-Objekt der IBAN als Parameter des Callback-Methodenaufrufs verpackt (Zeile 9).

Wichtige Hinweise

Die große Einschränkung von JSONP ist, dass es kein Fehlerhandling gibt. Wenn der Aufruf und das dynamische Hinzufügen des JavaScript-Codes klappt ist alles gut, wenn nicht passiert einfach gar nichts. Es gibt keine Fehlermeldung und auch kein 404 Code.
Gefährlich ist JSONP natürlich bei Services, die man nicht kennt. Man macht seine eigene Website damit anreifbar.

Schreibe einen Kommentar

Anmelden um einen Kommentar abzugeben.

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*