"Mein Name ist Mr. Wolf, ich löse Probleme." (PulpFiction)

Mittwoch, 26. Juni 2013

JavaScript WebService Consumer automatisch erstellen

Schon mal auf die Idee gekommen, einen WebService Client rein auf Basis von JavaScript zu erstellen?
Nun, warum nicht? Angeblich macht es Google auch so. Hab ich nicht nach geprüft. Für mich ist es mehr ein "proof of concept".
Was brauchen wir denn eigentlich:
  • Eine Verbindung zu einem WebService, sagen wir "http://myOwnPage.com/get_time/"
  • Eine SOAP Nachricht die wir diesem Service senden, und die er versteht, sprich Namespaces usw. müssen korrekt sein.
  • Die empfangene SOAP Nachricht muss gelesen und verstanden werden.
Mehr ist es eigentlich nicht. Legen wir also los:
Es ist möglich von einem Dokument eine Verbindung zu einer Adresse zu erstellen und dann Daten zu übertragen.
Die Klasse XMLHttpRequest die in den gängigen Browsern eingebaut ist macht uns die Sache sehr leicht:

var req = new XMLHttpRequest();
req.open('POST',"http://myOwnPage.com/get_time/",false)
req.send("<?xml version=\"1.0\" encoding=\"UTF-8\"?><soap-env:Envelope xmlns:soap-env...</soap-env:Envelope>");
alert(req.responseText);
Wir definieren einen Request (req) und sagen ihm mit welcher Adresse er sich verbinden soll. Das "false" als letzter Parameter sagt aus, dass die Verbindung synchron statt finden soll, wir erwarten also sofort eine Antwort.

Hier gleich mal ein Stolperstein: die Same Origin Policy (SOP) sagt aus, dass nur auf Dokumente in der gleichen Domain zugegriffen werden darf. Das heißt, der WebService muss praktisch auf dem selben Server liegen, sonst gibt es einen Fehler. Allerdings wird da auch dran gearbeitet und es gibt Lösungen. Eine zeige ich in den nächsten Posts.
Nun könnten wir uns also unser SOAP-Envelope zusammen basteln und los schicken. Die Antwort wird geparsed und reagiert.
Aber mal ehrlich, gibt's da nichts fertiges? Ich will doch nicht jetzt noch nen XML Parser basteln.
Siehe da, es gibt was fertiges, was sehr schönes sogar:
Das Apache CXF Projekt bietet ein Tool mit dem Namen: wsdl2js als Java Applikation. Dieses Tool liest eine WSDL Beschreibung und schreibt eine JavaScript Datei. In dieser Datei ist alles um mit dem WebService zu kommunizieren.
Beispiel gewünscht?
Nun bleiben wir bei unserem WebService: http://myOwnPage.com/get_time/
Haben wir diesen auf dem JBOSS deployed(gibts für dieses Wort eigentlich was deutsches?) so erhalten wir die WSDL unter der Adresse:

http://myOwnPage.com/get_time/?wsdl

Sagen wir weiter unser WebService bietet die Methode  

get_local_time(String City)

die den Namen einer Stadt erwartet und die aktuelle Uhrzeit vor Ort liefert. Dann wird in der WSDL diese Methode automatisch definiert und wsdl2js kann uns unser JavaScript liefern:

wsdl2js -d ./myJSProject/src http://myOwnPage.com/get_time/?wsdl

Mit dem -d sagen wir wohin die Datei abgelegt werden soll, und dann geben wir die Adresse der WSDL an. Ja, es kann die URL angegeben werden, die Datei wird automatisch herunter geladen.

Wenn alles richtig gelaufen ist, sollte es nun eine Datei mit dem Namen  
get_time_interfaceService.js
im entsprechenden Ordner geben. Diese Datei können wir dann auf unserer Seite einbinden
<script src="http://myOwnPage.com/src/get_time_interfaceService.js"><service>

Jetzt können wir direkt über JavaScript Funktionen mit dem WebService sprechen:

var get_time_WS;
get_time_WS = new get_time_interface();
get_time_WS.synchronous = true;
get_time_WS.url = "http://myOwnPage.com/get_time/"

Und die Verbindung ist hergestellt. So weit so gut, in dem Request  Beispiel von vorhin ging es schneller, aber jetzt wird es schön:
Wir brauchen zwei Funkionen, 
  • eine die einen eventuellen Fehler aufnimmt und 
  • eine für die eigentliche Antwort:
function errorCallback(httpStatus, httpStatusText) {
        globalErrorStatus = httpStatus; // integer HTTP status
        globalStatusText = httpStatusText; // Textual HTTP status
        alert(globalStatusText);
    }
function successCallback(responseObject) {
        // the parameter is an object of the type declared for the
        // method.
        globalResponseObject = responseObject;
    }   
Hier werden die Rückgabewerte direkt in globale Variablen geschrieben, was natürlich nicht so schön ist, aber für ein Beispiel ausreicht.
Aufrufen geht dann wie folgt:

get_time_WS.get_local_time(successCallback,errorCallback,"Berlin");
alert(globalResponseObject);

Es sollte nun die aktuelle Zeit aus Berlin ausgegeben werden.
Das Schöne an der Methode ist die Einfachheit. Man erhält ein Objekt zurück, in diesem Fall einen String. Sind in der WSDL  komplexe Typen vorhanden, Klassen oder Arrays z.B., werden diese auch in ein JavaScript Objekt umgewandelt und man kann direkt mit ihnen weiter arbeiten. Die Parameter der Methode werden in einen SOAP-Envelope verpackt und abgeschickt, das Ergebnis geparsed und nur der eigentliche Inhalt zurück gegeben. Keinerlei Arbeit.
Einen Nachteil hat die Methode, sie unterstützt leider kein WebService Login, dies müsste man sich dann selber einbauen.

In der entstandenen .js Datei können direkt die Methoden eingesehen werden, und diese werden von Eclipse auch beim tippen vervollständigt. Außerdem braucht man sich beim erweitern des WebService keine Gedanken zu machen, ob die JavaScript Seite noch funktioniert. Ändert man nichts am bestehenden Interface, sondern erweitert es nur, so kann man wsdl2js noch einmal laufen lassen und die entstandene .js direkt verwenden.

PS: Die Beispiele hier wurden nie getestet. Ich habe funktionierenden Code nachgebildet, da ich den eigentlichen Code nicht veröffentlichen möchte. Sollte also jemand Verbesserungsvorschläge haben, immer her damit.

Keine Kommentare:

Kommentar veröffentlichen