Seite 1 von 2 12 LetzteLetzte
Ergebnis 1 bis 15 von 30
  1. #1
    Avatar von jspit
    jspit ist offline Lounge-Member
    registriert
    19-06-2009
    Beiträge
    1.710

    Cross-Domain AJAX mit JSONP

    Viele kennen das Problem, wenn man mal schnell per Javascript/Ajax einige Daten von einen fremden Server holen möchte und die Same Origin Policy (SOP) dies verhindert. JSONP heist die Lösung, mit dem par JS/AJAX über Domaingrenzen hinweg Webservices aufgebaut werden können.
    In diesen Beitrag soll die Funktion von JSONP kurz erläutert und eine eigenständige JS-Funktion für JSONP vorgestellt werden, die eine von Frameworks unabhängige Nutzung ermöglicht. JSONP nutzt dafür die Tatsache, das Scripte von fremden Servern nicht der SOP unterliegen. Die Informationsauswahl erfolgt per GET über Parameter. Dies können wir ganz einfach per Eingabe in der Browserzeile testen. Für die Beispiele nutzen wir die freie geographische Datenbank GEONames. Nach der Eingabe von

    Code:
    http://ws.geonames.org/postalCodeLookupJSON?postalcode=55116&country=DE&callback=myfkt
    sollte folgende Antwort zurückkommen:
    Code:
    myfkt({"postalcodes":[{"adminCode3":"07315","adminName3":"Mainz, kreisfreie Stadt","adminCode2":"00","postalcode":"55116","adminCode1":"RP","countryCode":"DE","lng":8.26882307692308,"placeName":"Mainz","lat":50.0009769230769,"adminName1":"Rheinland-Pfalz"}]});
    Wir erhalten einen script für den Aufruf der function myfkt mit unseren gewünschten Informationen in einem Object als Parameter. Bevor wir diesen Funktionsaufruf jedoch zur Ausführung bringen, muß die Function definiert werden:
    PHP-Code:
    function myfkt(obj) {
    // Mache etwas mit den obj-Daten 

    In der Callbackfunktion myfkt ist es dem Nutzer überlassen, was er mit den Daten anstellt. Für die Ausführung der Anfrage wird ein script-Element mit entsprechenden Inhalt erstellt, welches dynamisch in den head eingeklinkt wird und unsere myfkt zur Ausführung bringt. Das ist schon der ganze Zauber.

    Unsere eigene Funktion jsonp soll die Nutzung etwas vereinfachen und benötigt 3 Parameter:
    1. surl: Die url von unserem Webservice, z.B. "http://ws.geonames.org/postalCodeLookupJSON?"
    2. par : Ein Object mit den Parametern
    3. myfkt: unsere Callbackfunktion
    Die Parameter sind etsprechend des Webservices spezifisch. Für den 'postalCodeLookupJSON' könnte das Parameterobject wie folgt aussehen:

    PHP-Code:
      var par = {
        
    callback"?",
        
    country"DE",
        
    postalcode55116
      
    }; 
    Bei callback muß ein "?" benutzt werden. Intern wird eine eigene eindeutige Callbackfunction gebildet, die unsere myfkt aufruft und dann noch ein paar Aufräumarbeiten erledigt. Hier nun der Code unserer Funktion:

    PHP-Code:
    function jsonp(surl,par,myfkt){
      var 
    nds;
      if(
    typeof(pjs)=="undefined"pjs={};
      var 
    callback="json" parseInt(Math.random()*100000);
      
    pjs[callback] = function (jobj){
        
    myfkt(jobj);
        
    document.getElementsByTagName('head')[0].removeChild(nds);
        
    delete pjs[callback];
      }
      var 
    murl=surl;
      if(
    typeof par == "object") {
        for (var 
    p in parmurl += "&" "=" par[p];
        }
      var 
    p=murl.indexOf("callback=?");
      if(
    >= 0)murl=murl.slice(0,p+9)+ "pjs." callback murl.slice(p+10);
      var 
    nd document.createElement('script');
      
    nd.setAttribute('type','text/javascript');
      
    nd.setAttribute('src',murl);
      
    nds document.getElementsByTagName('head')[0].appendChild(nd);

    Nachfolgend noch ein komplettes Beispiel:
    HTML-Code:
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
    <title>Test JSONP</title>
    <script type="text/javascript">
    function jsonp(surl,par,myfkt){
      var nds;
      if(typeof(pjs)=="undefined") pjs={};
      var callback="json" + parseInt(Math.random()*100000);
      pjs[callback] = function (jobj){
        myfkt(jobj);
        document.getElementsByTagName('head')[0].removeChild(nds);
        delete pjs[callback];
      }
      var murl=surl;
      if(typeof par == "object") {
        for (var p in par) murl += "&" + p + "=" + par[p];
        }
      var p=murl.indexOf("callback=?");
      if(p >= 0)murl=murl.slice(0,p+9)+ "pjs." + callback + murl.slice(p+10);
      var nd = document.createElement('script');
      nd.setAttribute('type','text/javascript');
      nd.setAttribute('src',murl);
      nds = document.getElementsByTagName('head')[0].appendChild(nd);
    }
    //Eigene Function, die etwas mit den Informationen macht
    function myfkt(obj) {
    	var mydiv=document.getElementById("city");
        if(obj && obj.postalcodes) {
            if(obj.postalcodes[0] && obj.postalcodes[0].placeName) mydiv.innerHTML = obj.postalcodes[0].placeName;
            else mydiv.innerHTML = "nichts gefunden";
        }
        else {
            if(obj && obj.status) mydiv.innerHTML = obj.status.message;
            else mydiv.innerHTML = "unbekannte Antwort";
        }
    }
    
    function abfrage(){
      document.getElementById("city").innerHTML = "Anfrage in Arbeit...";
      var webservice = 'http://ws.geonames.org/postalCodeLookupJSON?';
      var plz=document.getElementById("plz").value;
      var par = {
        callback:"?",
        country:"DE",
        postalcode: plz
      };
      jsonp(webservice, par , myfkt);
    }
    </script>
    </head>
    <body>
    <h1>Test Cross-Domain AJAX mit JSONP</h1>
    <div id="city">Stadt</div>
    <br>
    PLZ: <input type="text" id="plz">
    <button onclick="abfrage()">abfragen</button>
    </body>
    </html>
    Der Code erhebt kein Anspruch auf Vollkommenheit, soll Anregungen für eigene Versuche geben. Kritiken und Kommentare sind willkommen.

    LG jspit
    Geändert von jspit (11-01-2011 um 22:07 Uhr)

  2. #2
    Avatar von kkapsner
    kkapsner ist offline Super Moderator
    registriert
    28-03-2008
    Beiträge
    17.677

    AW: Cross-Domain AJAX mit JSONP

    An sich kein schlechter Ansatz - ich hab' nur drei Punkte:
    1. Warum erzeugst du eine neue globale Variable? Das ist völlig unnötig, da du ja schon ein globales Objekt hast... deine Funktion jsonp. In dieser kannst du deine Callbacks genauso ablegen und aufrufen und verschmutzt den globalen Namespace nicht weiter.
    2. deine Sache mit dem indexOf("callback=?") ist etwas unglücklich - überprüfe doch vorher, ob par.callback == "?" und ersetzte das dann schon vor dem Zusammenbauen der URL.
    3. die Parameter gehören unbedingt durch encodeURIComponent gejagt - sonst kannst du mit Steuerzeichen und Sonderzeichen schnell Probleme bekommen.

    Hier meine Verbesserung:
    Code:
    function jsonp(url, par, myfkt){
    	if(par && typeof par == "object") {
    		if (par.callback == "?"){
    			var callback = "json" + jsonp.callbackCount++;
    			jsonp[callback] = function (jobj){
    				myfkt(jobj);
    				nd.parentNode.removeChild(nd);
    				delete jsonp[callback];
    			}
    			par.callback = "jsonp." + callback;
    		}
    		for (var p in par) url += "&" + encodeURIComponent(p) + "=" + encodeURIComponent(par[p]);
    	}
    	var nd = document.createElement('script');
    	nd.setAttribute('type','text/javascript');
    	nd.setAttribute('src',url);
    	document.getElementsByTagName('head')[0].appendChild(nd);
    }
    jsonp.callbackCount = 0;

  3. #3
    Avatar von jspit
    jspit ist offline Lounge-Member
    registriert
    19-06-2009
    Beiträge
    1.710

    AW: Cross-Domain AJAX mit JSONP

    Zitat Zitat von kkapsner Beitrag anzeigen
    1. Warum erzeugst du eine neue globale Variable? Das ist völlig unnötig, da du ja schon ein globales Objekt hast... deine Funktion jsonp. In dieser kannst du deine Callbacks genauso ablegen und aufrufen und verschmutzt den globalen Namespace nicht weiter.
    Ok. Gute Idee.
    Zitat Zitat von kkapsner Beitrag anzeigen
    2. deine Sache mit dem indexOf("callback=?") ist etwas unglücklich - überprüfe doch vorher, ob par.callback == "?" und ersetzte das dann schon vor dem Zusammenbauen der URL.
    Das Ersetzen im String mache ich, damit der Nutzer wahlweise auch (fixe) Parameter mit an die url hängen kann und für par=null das Ganze dann auch noch funktioniert.
    Edit: par.callback fällt aus, da auch Namen wie jsonpcallback benötigt werden.

    Zitat Zitat von kkapsner Beitrag anzeigen
    3. die Parameter gehören unbedingt durch encodeURIComponent gejagt - sonst kannst du mit Steuerzeichen und Sonderzeichen schnell Probleme bekommen.
    Das kann ja nicht schaden, obwohl Steuerzeichen und Sonderzeichen dann wohl noch an anderen Stellen Probleme bereiten können und ich nie auf die Idee kommen würde hier so etwas zu benutzen.
    Edit: es dürfte reichen die Parameter selbst mit encodeURIComponent zu behandeln, die Namen p unterliegen ja schon strengeren Regeln.

    Neben den 3.Punkten hast du aber noch weitere Alternativen in deinem Code (z.B.counter), die ich mir nochmal durch den Kopf gehen lasse. Erstmal vielen Dank.

    Ein kleines Problem besteht noch:
    Ich weiss nicht ob bei allen JSONP-Services der Name für den Parameter der callbackfunktion auch auf callback endet oder dies noch variabel gestaltet werden sollte.
    Geändert von jspit (12-01-2011 um 19:47 Uhr) Grund: Ergänzung

  4. #4
    Avatar von kkapsner
    kkapsner ist offline Super Moderator
    registriert
    28-03-2008
    Beiträge
    17.677

    AW: Cross-Domain AJAX mit JSONP

    OK - ich hab' eine Idee für die verschiedenen Namen des Callbacks (poste jetzt mal einfach den Code aus meinem Framework):
    Code:
    (function(){
    
    /**
     * Object kkjs.JSONP
     * @name: kkjs.JSONP
     * @author: Korbinian Kapsner
     * @description: provides JSONP functionality
     */
    
    kkjs.JSONP = {
    	process: function(url, parameter){
    		var script = kkjs.node.create({
    			tag: "script",
    			type: "text/javascript",
    		});
    		var value;
    		if(kkjs.is.object(parameter)){
    			for (var name in parameter){
    				value = parameter[name];
    				if (kkjs.is.function(value)){
    					value = this.createCallback(value, script);
    				}
    				url += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(value);
    			}
    		}
    		script.src = url;
    		document.getElementsByTagName('head')[0].appendChild(script);
    	},
    	
    	callback: {},
    	createCallback: function(callbackFn, script){
    		var callback = "_" + this.callbackCount.toString(36);
    		this.callbackCount++;
    		this.callback[callback] = function (jobj){
    			callbackFn(jobj);
    			if (script.parentNode) script.parentNode.removeChild(script);
    			delete this[callback];
    		}
    		return "kkjs.JSONP.callback." + callback;
    	},
    	callbackCount: 0
    };
    
    }).apply()
    - dein Beispiel würde dann so aussehen:
    Code:
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
    <title>Test JSONP</title>
    <script type="text/javascript" src="http://kkjs.kkapsner.de/modules/kkjs.load.js?modules=JSONP"></script>
    <script type="text/javascript">
    
    //Eigene Function, die etwas mit den Informationen macht
    function myfkt(obj) {
    	var mydiv=document.getElementById("city");
        if(obj && obj.postalcodes) {
            if(obj.postalcodes[0] && obj.postalcodes[0].placeName) mydiv.innerHTML = obj.postalcodes[0].placeName;
            else mydiv.innerHTML = "nichts gefunden";
        }
        else {
            if(obj && obj.status) mydiv.innerHTML = obj.status.message;
            else mydiv.innerHTML = "unbekannte Antwort";
        }
    }
    
    function abfrage(){
    	document.getElementById("city").innerHTML = "Anfrage in Arbeit...";
    	kkjs.JSONP.process(
    		'http://ws.geonames.org/postalCodeLookupJSON?',
    		{
    			callback: myfkt,
    			country:"DE",
    			postalcode: document.getElementById("plz").value
    		}
    	);
    }
    </script>
    </head>
    <body>
    <h1>Test Cross-Domain AJAX mit JSONP</h1>
    <div id="city">Stadt</div>
    <br>
    PLZ: <input type="text" id="plz">
    <button onclick="abfrage()">abfragen</button>
    </body>
    </html>
    Fixe Parameter kann der Nutzer ja immer noch im url-Parameter angeben - die anderen werden da ja nur mit "&..." angehängt.
    Sonderzeichen können bei deinem Beispiel sogar schon eine Rolle spielen - wenn man zu einer Stadt eine PLZ sucht (z.B. München oder Köln) - sollten also auf jeden Fall beachtet werden.

    PS:
    Code:
    alert(typeof null);

  5. #5
    Avatar von jspit
    jspit ist offline Lounge-Member
    registriert
    19-06-2009
    Beiträge
    1.710

    AW: Cross-Domain AJAX mit JSONP

    Die leicht erweiterte Function jsonp arbeitet jetzt mit einem zusätzlichen Timeout für die Anfrage. Bei Ablauf des Timeouts wird eine Fehlermeldung in die Nutzerfunktion callbackfct zurückgegeben. Das Timeout in ms kann als optionaler Parameter angegeben werden. Mit einem Timeout von 0 wird die Überwachung ausgeschaltet, ohne den Parameter wird der Defaultwert von 5000 (5 Sekunden) benutzt. Wird das Timeout zu klein gewählt, kann dies auch 2 Aufrufe der callbackfct zur Folge haben.
    Der Aufruf von jsonp hat eine veränderte Reihenfolge der Parameter und ist jetzt wie folgt:

    nr = jsonp(url,callbackfct,[par],[timeout])

    Der Rückgabewert ist die laufende Nummer der Anfrage, die auch mit jsonp.callbackCount global verfügbar ist. Für die Identifizierung des callback-Parameters wird intern der String CallbackIdent="callback" benutzt, es werden so alle Namen erkannt die auf 'callback' enden, wie z.B. 'jsonpCallback'. Der Parameter selbst muß das Fragezeichen sein.
    Beispielaufruf:
    Code:
    jsonp(
    	'http://ws.geonames.org/postalCodeLookupJSON?callback=?&country=DE',
    	myfkt,
    	{postalcode: 55116}
    )
    Die Parameter können wahlweise in der url oder im optionalen Parameterobject übergeben werden. Bei einem Webservice mit außergewöhnlichen Namen für den callback kann auch CallbackIdent="" gesetzt werden, die Identifizierung erfolgt dann nur durch das '?'.

    Nachfolgend der Code für die erweiterte Function, in der auch einige Ideen von kkapsner verarbeitet wurden:
    PHP-Code:
    function jsonp(url,callbackfct,par,timeout){
        
    //jsonp(url,callbackfct,[par],[timeout])
        //Die folgenden 2 Zeilen bei Bedarf anpassen
        
    var MsgTimeout = {"status":{"message":"Error Timeout"}};  //Daten(Meldung) bei Timeout
        
    var CallbackIdent "callback";    //String zur Identifizierung des Callback-Parameters 
        //
        
    if(typeof timeout != 'number'timeout=5000;
        if(
    typeof par == 'number'timeout=par;
        
    jsonp.callbackCount = (typeof jsonp.callbackCount == "number") ? ++jsonp.callbackCount 0;
        var 
    callback "json" jsonp.callbackCount;
        
    jsonp[callback] = function (jobj){
            
    window.clearTimeout(wt);
            
    callbackfct(jobj);
            
    nd.parentNode.removeChild(nd);
            
    delete jsonp[callback];
        }
        var 
    p=url.toLowerCase().indexOf(CallbackIdent +"=?");
        if(
    >= 0) {
            var 
    cl=CallbackIdent.length 2;
            
    url=url.slice(0,p+cl-1)+ "jsonp." callback url.slice(p+cl);
        }
        if(
    par && typeof par == "object") {
            for (var 
    pn in par) {
                if(
    pn.toLowerCase().indexOf(CallbackIdent) >= && par[pn]=="?"url += "&" pn "=jsonp." callback;
                else 
    url +=  "&" pn "=" encodeURIComponent(par[pn]) ;
            }
        }
        var 
    nd document.createElement('script');
        
    nd.setAttribute('type','text/javascript');
        
    nd.setAttribute('src',url);
        var 
    wt 0
        if(
    timeout 0wt window.setTimeout(function(){callbackfct(MsgTimeout)},timeout);
        
    document.getElementsByTagName('head')[0].appendChild(nd);
        return 
    jsonp.callbackCount;


  6. #6
    Avatar von kkapsner
    kkapsner ist offline Super Moderator
    registriert
    28-03-2008
    Beiträge
    17.677

    AW: Cross-Domain AJAX mit JSONP

    ... das mit dem Timeout hatte ich mir auch schon überlegt. Aber sonst ganz gut - was ich anders machen würde steht ja oben...

  7. #7
    Avatar von jspit
    jspit ist offline Lounge-Member
    registriert
    19-06-2009
    Beiträge
    1.710

    AW: Cross-Domain AJAX mit JSONP

    Noch eine Anmerkung zum Thema Sicherheit von JSONP.
    Ich habe Sätze gelesen wie diese:'dafür nutzt JSONP eine Sicherheitslücke im Browser'. Das ist so formuliert Unsinn. JSONP nutzt die Tatsache, daß Javascripte von Fremddomains eingebunden werden können, also nicht der SOP unterliegen. Daran wird sich vermutlich auch nichts ändern, denn viele APIs (u.a. Google Maps) nutzen diese Tatsache aus.
    JSONP ist damit mindestens genauso sicher wie jeder andere javascript, der von einer fremden Domain eingebunden wird. Man muss also den Anbieter vertrauen, das dieser nicht bösartigen JavaScript statt des erwarteten JSON-Objectes zurückgibt. Da aber die Function zur Verarbeitung des JSON von einem selbst stammt, hat man es auch in der Hand, die JSON-Daten zu prüfen und nicht z.B. mit eval blind zu verarbeiten.

  8. #8
    Avatar von kkapsner
    kkapsner ist offline Super Moderator
    registriert
    28-03-2008
    Beiträge
    17.677

    AW: Cross-Domain AJAX mit JSONP

    Das Argument mit der eigenen Funktion zur Weiterverarbeitung ist nicht haltbar:
    Code:
    callback({
    	index: (function(){
    		alert("I am evil!"); return 3;
    	};)()
    });

  9. #9
    ein schlauer ist offline Lounge-Member
    registriert
    18-08-2004
    Beiträge
    14.671

    AW: Cross-Domain AJAX mit JSONP

    Zitat Zitat von jspit Beitrag anzeigen
    Da aber die Function zur Verarbeitung des JSON von einem selbst stammt, hat man es auch in der Hand, die JSON-Daten zu prüfen und nicht z.B. mit eval blind zu verarbeiten.
    Machst du doch auch nicht, sondern du bindest das Skript mit einem script-Element ein, d.h. du kannst da nichts kontrollieren. Deshalb muss das Vertrauen gross sein.

  10. #10
    Avatar von kkapsner
    kkapsner ist offline Super Moderator
    registriert
    28-03-2008
    Beiträge
    17.677

    AW: Cross-Domain AJAX mit JSONP

    ... ist aber genauso wie bei normal eingebundenen Scripts von anderen Seiten. Aber ich finde JSONP einen guten Ansatz, wenn man selbst zwei Orte im Netz hat, die wegen der SOP nicht normal miteinander kommunizieren können (z.B. zwei verschiedene Subdomains...).

  11. #11
    ein schlauer ist offline Lounge-Member
    registriert
    18-08-2004
    Beiträge
    14.671

    AW: Cross-Domain AJAX mit JSONP

    Hab' ja nichts dagegen gesagt, nur darauf hingewiesen, das hier nicht eval zum Einsatz kommt

  12. #12
    Avatar von jspit
    jspit ist offline Lounge-Member
    registriert
    19-06-2009
    Beiträge
    1.710

    AW: Cross-Domain AJAX mit JSONP

    Zitat Zitat von kkapsner Beitrag anzeigen
    Das Argument mit der eigenen Funktion zur Weiterverarbeitung ist nicht haltbar:
    Code:
    callback({
    	index: (function(){
    		alert("I am evil!"); return 3;
    	};)()
    });
    Ok, das Beispiel überzeugt. Man hat es zwar in der Hand die Daten zu prüfen bevor sie weiterverarbeitet werden, kann jedoch nicht verhindern daß Schadcode ausgeführt wird und "Deshalb muss das Vertrauen gross sein.".

  13. #13
    ein schlauer ist offline Lounge-Member
    registriert
    18-08-2004
    Beiträge
    14.671

    AW: Cross-Domain AJAX mit JSONP

    Du brauchst ja noch nicht mal die callback Funktion, die dient ja nur zur Kommunikation mit der Ursprunglichen Seite. Es wird jeder JS Code ausgeführt, in dem eingebundenen Skript, deshalb ...

  14. #14
    Avatar von jspit
    jspit ist offline Lounge-Member
    registriert
    19-06-2009
    Beiträge
    1.710

    AW: Cross-Domain AJAX mit JSONP

    Das ist wohl wahr. Ich würde aber ein Timeoutfehler bekommen, bei kkapsner's Beispiel dies aber noch nicht mal merken wenn dies mit reingeschummelt wird (gemein!).

  15. #15
    Avatar von Junkee[]
    Junkee[] ist offline Lounge-Member
    registriert
    08-05-2009
    Ort
    Leonberg
    Beiträge
    3.169

    AW: Cross-Domain AJAX mit JSONP

    Habe versucht ein CommonJS Modul dafür zu schreiben aber versage daran, dass man den Funktionsnamen kennen muss. Vorschläge, wie man den Funktionsnamen heraus bekommt oder, was mir lieber währe, wie man umgehen kann, dass der Funktionsname gebraucht wird?

Seite 1 von 2 12 LetzteLetzte

Ähnliche Themen

  1. cross domain "ajaxing"
    Von ToM80 im Forum JavaScript
    Antworten: 0
    Letzter Beitrag: 14-09-2010, 08:30
  2. Antworten: 1
    Letzter Beitrag: 05-08-2010, 12:19
  3. Ajax: cross Site http-request
    Von Tachyon im Forum JavaScript
    Antworten: 11
    Letzter Beitrag: 07-07-2010, 14:14
  4. Cross-Domain Request Problem umgehen
    Von tobiasgruen im Forum JavaScript
    Antworten: 3
    Letzter Beitrag: 22-03-2007, 12:02
  5. Cross Domain Scripting
    Von AgentSmith im Forum JavaScript
    Antworten: 9
    Letzter Beitrag: 21-07-2005, 16:17

Stichworte

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •