Back to top

Web Service (SOAP & WSDL)

Web Service (WS) - moim zdaniem rewolucja. Kiedy pierwszy raz spotkałem się z tą technologią to zupełnie jej nie doceniłem. Stary slogan Sun Microsystems głosił: "dopiero sieć to komputer" - chcąc wykorzystać podobny chwyt w promocji WS musiałbym chyba napisać: "dopiero sieć to program" :) I mniej więcej o to chodzi. Wykorzystanie zdalnych usług sieciowych "jeszcze nigdy nie było tak proste" (rasowy handlowiec ze mnie, co?), teraz już nie musimy martwić się o obsługę protokołu, transmisji, wysyłanie i odbieranie danych - wszystko to zostało schowane pod podszewką WS, trzeba tylko wyciągnąć rękę, zerwać i cieszyć się smakiem :)

WS to produkt cząstkowy - nie stanowi rozwiązania, czy gotowego produktu z punktu widzenia użytkownika końcowego. To tylko narzędzie, idea, polegająca na standaryzacji protokołu wymiany komunikatów pomiędzy klientem a serwisem. Tak mało, a zarazem tak wiele - standaryzacja to jednak zawsze dobry pomysł (przynajmniej w odniesieniu do technologii). Mamy więc komunikację opisaną standardem - protokół SOAP, co z tego wynika? Na razie niewiele, ale to jeszcze nie koniec. Aby życie było jeszcze prostsze postanowiono wymyślić coś tak fajnego jak WSDL - czyli opis serwisu w standaryzowanym formacie XML. I teraz podsumujmy - mamy opis (WSDL), mamy protokół transportowy (SOAP) - to już naprawdę mocna rzecz, przykłady za chwilę!

Zdalny Web Service dostępny jest w naszym lokalnym programie jako obiekt (instancja klasy opisanej przez opis WSDL). Spójrzmy na poniższy kod:

python (https://github.com/lbacik/fortune-ws-clients/blob/master/python/client.py):


from suds.client import Client
c = Client('http://luka.sh/fortune/tools/soap.php?WSDL&readable')
print (c.service.getFortuneStr('debian'))

php (https://github.com/lbacik/fortune-ws-clients/blob/master/php/client2.php):


$c = new SoapClient('http://luka.sh/fortune/tools/soap.php?WSDL');
echo $c->__call('getFortuneStr', array('debian'));

c# (https://github.com/lbacik/fortune-ws-clients/blob/master/cs/client/Progr...):


using System;

namespace client
{
    class Program
   {
       static void Main(string[] args)
       {
           FortuneService.FortuneSrvSoapClient f = new FortuneService.FortuneSrvSoapClient();
           Console.WriteLine(f.getFortuneStr("debian"));
       }
   }
}

(c# wymaga jeszcze wskazania FortuneSrvSoapClient jako WS - to jest do przeklikania w ustawieniach projektu)

Wszystkie powyższe przykłady (jak wskazują linki powyżej) zapisałem w repozytorium: https://github.com/lbacik/fortune-ws-clients

Zen. Aby uczcić tę chwilę, pozwolę sobie na cytat z książki "Zwinny samuraj..." Jonathana Rasmussona (proszę tylko nie myślcie, iż poniższe sformułowanie"Mistrz" ma jakieś konotacje z moją skromną osobą - to tylko formułka, która bardzo mi się spodobała :)):

Patrzcie na Mistrza, podążajcie za mistrzem, kroczcie z mistrzem, przejrzyjcie mistrza, zostańcie mistrzami.

W tych kilku powyższych linijkach kodu stworzyliśmy instancję klasy (obiekt),wg definicji WSDL umieszczonej pod adresem: http://luka.sh/fortune/tools/soap.php?WSDL&readable, następnie wywołaliśmy metodę tego obiektu (metodę: getFortuneStr(), wykorzystując protokół SOAP), a na koniec wynik wykonania wypisaliśmy na konsolę. To dużo jak na dwie linijki kodu… Wszystkie szczegóły dotyczące komunikacji są przed nami ukryte - my po prostu tworzymy instancję klasy i dalej posługujemy się już tak otrzymanym obiektem, tak jakby obiekt ten był zdefiniowany lokalnie :) Oczywiście program będzie miał problemy z działaniem offline, to istotne ograniczenie i trzeba mieć to na uwadze - WS nie przyda nam się przy realizacji projektów nie opartych o komunikację sieciową. Jednak jeżeli nasz projekt realizuje/korzysta z jakiś usług sieciowych to z WS będzie nam jak najbardziej po drodze. Istnieje tendencja zastępowania autorskich protokołów wykorzystywanych przez usługi sieciowe (np. realizującymi jakieś usługi biznesowe) poprzez interfejsy oparte na WS, i to chyba dobry kierunek. Być może jesteśmy nawet świadkami narodzin nowej formy www? Możliwości wykorzystania WS pobudzają wyobraźnię! Całkiem możliwe, że rewolucja w www się zbliża - pożyjemy zobaczymy.

Spójrzmy na stronę serwisu (endpoint uri): http://luka.sh/fortune/tools/soap.php - to co zobaczymy (o ile spróbujemy otworzyć tę stronę w przeglądarce) ma ścisły związek z wykorzystaną do budowy serwisu technologią, w przypadku serwisu zbudowanego w php z wykorzystaniem biblioteki PhpWsdl, strona będzie wyglądać podobnie do powyżej wskazanej - w przypadku innych technologii "wygląd" może być zupełnie inny. Opis wsdl serwisu znajdziemy pod adresem: http://luka.sh/fortune/tools/soap.php?WSDL. Na stronie serwisu udostępniany jest jeszcze adres do kodu klienta php: http://luka.sh/fortune/tools/soap.php?PHPSOAPCLIENT - można pobrać automatycznie wygenerowaną klasę, gotową do wykorzystania w naszym programie. To trochę inny sposób (bardziej… rozbudowany) wykorzystania WS niż ten prezentowany w przykładzie dla php powyżej (choć to tylko kwestia konwencji) - mamy dowolność, implementację obu sposobów umieściłem w źródłach przykładowych klientów php serwisu Fortune: https://github.com/lbacik/fortune-ws-clients/tree/master/php.

Mając dostęp do serwisu (endpoint uri) możemy rozpocząć pisanie kodu go wykorzystującego (definicja wsdl może być dostępna online, albo dostarczona w formie pliku) , ale czy możemy przetestować serwis bez konieczności pisania programu klienckiego? Okazuje się, że jak najbardziej tak - znajdziemy odpowiednie narzędzia w Internecie. Zanim jednak przejdę do krótkiego omówienia jednego z tych narzędzi, chcę zwrócić uwagę na CLI Pythona - możemy doskonale wykorzystać ów CLI w testowaniu WS, co polecam wszystkim tym którzy Pythona znają i lubią (nie różni się to niczym od kodu Pythona zaprezentowanego w przykładzie powyżej, choć oczywiście daje nam możliwość interakcji z tak utworzonym obiektem w interaktywnej powłoce - przykłady z wykorzystaniem biblioteki suds są dostępne np. tutaj: https://fedorahosted.org/suds/wiki/Documentation - polecam jeżeli ktoś interesuje się Pythonem!).

A jeżeli nie jesteśmy programistami możemy wykorzystać SOAPUI - http://www.soapui.org/ (poza wersją pro jest dostępna wersja darmowa SOAPUI, która w zupełności wystarczy nam do testowania WS). Instalujemy, uruchamiam - na stronie: http://www.soapui.org/Getting-Started/your-first-soapui-project.html znajdziemy "step by step guide" pokazujący jak stworzyć nasz pierwszy projekt w SOAPUI. Po konfiguracji adresu opisu wsdl serwisu program automatycznie wygeneruje listę dostępnych metod (udostępnianych przez dany serwis), a po wybraniu danej metody zostanie wygenerowany szablon (w xml) jej wywołania - znaki zapytania oznaczają miejsca które musimy wypełnić / uzupełnić przed "wysłaniem" wywołania do serwisu. Po wysłaniu "requestu" program wyświetli otrzymaną (w formacie xml) odpowiedź. Rysunek (zrzut ekranu) dołączony do tego artykułu pokazuje SOAPUI w akcji z serwisem Fortune - w okienku interfejsu widać wywołanie metody getFortuneStr(), oraz otrzymaną z serwisu odpowiedź - fortunkę :)

A jak wygląda sytuacja po stronie serwera? Również dość prosto - kod "fortunkowego" serwisu można podejrzeć na: https://github.com/lbacik/fortune-ws, cała "magia" to plik fortuneSoap.php. Pamiętajmy, iż w tym konkretnym przypadku została wykorzystana biblioteka PhpWsdl (niestety dość już "leciwa", ostatnia wersja została wydana w 2011 roku i (ponownie niestety) ma to dość istotne znaczenie, o czym za chwilkę). Osobiście aktualnie wstrzymuję się z "przełączeniem" na inną bibliotekę, bo ciągle jeszcze nie podjąłem decyzji czy zostaję przy Php, czy też "fortunkowy" serwis zmigruję do Pythona… W większości przypadków biblioteka PhpWsdl okaże się zupełnie wystarczającym narzędziem i pewnie nawet nie podejrzewałbym problemów związanych z jej wykorzystaniem, gdyby nie próba napisania klienta w Javie - oczywiście zupełnie nieudana. Abstrahując od skomplikowanego mechanizmu "łączenia" z WS w przypadku Javy - opiszę go jak tylko uda mi się już połączyć z poziomu Javy z "fortunkowym" serwisem - poszukiwanie przyczyny niepowodzenia doprowadziło mnie do bardzo ciekawego artykułu opisującego pewną "ewolucję" w formacie opisu WSDL. Artykuł można znaleźć tutaj: http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/. W skrócie - aktualnie mamy pięć typów opisów wsdl: RPC/encoded, RPC/literal, Document/encoded, Document/literal oraz "Document/literal wrapped" - wszystkie poza dwoma ostatnimi nie przechodzą walidacji XML, Java odrzuca te "typy" które się nie walidują, a niestety PhpWsdl generuje opis wsdl typu "RPC/encoded" - ot i cały problem…

Na koniec warto coś powiedzieć o alternatywach. Jako niedoskonałość protokołu SOAP często wymienia się dość duży narzut komunikacyjny - niestety komunikaty (nie mówię o danych) przesyłane pomiędzy klientem a "endpointem" usługi (serwerem) przy wykorzystaniu opartego na XML SOAP-a, mogą, szczególnie przy dużej aktywności połączenia, stanowić znaczący procent wykorzystania łącza. Cóż - coś za coś, zyskujemy elastyczność, płacimy za to przepływnością, ale czy nie moglibyśmy zyskać elastyczności za niższą cenę? W tym kontekście można wspomnieć o takim protokole jak JSON-RPC, opartym na standardzie JSON rozwiązaniu którego zapotrzebowanie na pasmo, ze względu na znacznie oszczędniejszą notację, jest dużo mniejsze. Co prawda SOAP zachowuje znacznie większą elastyczność jeżeli chodzi o wykorzystanie złożonych typów/struktur danych, ale JSON-RPC może przydać się w sytuacjach, w których zależy nam na optymalizacji komunikacji pod kątem ilości przesyłanych meta-danych. Jak zwykle dobranie odpowiedniego rozwiązanie zależy od konkretnego problemu, a ponieważ z JSON-RPC jak do tej pory nie zmierzyłem się w praktyce, dlatego aktualnie nie będę się zagłębiał w jego analizę i porównanie z SOAP - coś trzeba sobie zostawić na później! :)

I to by było chyba na tyle, na koniec jeszcze raz powtórzmy refren (wszyscy razem!):

Patrzcie na Mistrza, podążajcie za mistrzem, kroczcie z mistrzem, przejrzyjcie mistrza, zostańcie mistrzami.