W tym artykule pokażę, w jak prosty sposób wywoływać metody webserwisów opartych o SOAP w aplikacji mobilnej na system Android. W tym konkretnym przykładzie posłużymy się API systemu PayPal, jednak pokazane tutaj metody, sprawdzą się z dowolnym webserwisem SOAP.
Czym jest Webserwis?
Zanim przejdziemy do mięsa, warto wspomnieć czym jest webserwis. Określenie to (usługa internetowa) odnosi się do aplikacji udostępnionej w sieci (zwykle w Internecie), z którą mogą łączyć się inne aplikacje i korzystać z jej zasobów.
Usługi internetowe są obecnie bardzo popularne i praktycznie każdy większy dostawca oprogramowania udostępnia API w formie webserwisu. Dzięki temu w łatwy sposób możemy tworzyć aplikacje na telefon wyświetlające informacje z Facebooka, Twittera, pokazujące pogodę itp. Te wszystkie dane są pobierane przez aplikację mobilną z webserwisu.
Czym jest SOAP?
Skoro wiemy już czym jest usługa internetowa, to czas zapoznać się z rodzajami takich usług. Mamy dwa główne standardy tworzenia webserwisów:
- REST (Representational State Transfer)
- SOAP (Simple Object Access Protocol)
Nie wchodząc w szczegóły, SOAP był szeroko stosowany kilka-kilkanaście lat temu. Obecnie królują webserwisy REST i większość firm tworzy usługi internetowe właśnie w tym standardzie. Pomimo, iż obecnie REST jest w modzie, to przez ostatnie kilkanaście lat wiele firm stworzyło web serwisy SOAP, więc nadal, pisząc aplikację mobilną, możemy mieć konieczność korzystania właśnie z nich i o tym jest niniejszy post.
Jeśli interesuje Cię co spowodowało, że SOAP został zastąpiony RESTem, to już spieszę z odpowiedzią, gdyż jest ona mocno związana z treścią tego artykułu.
Problemy SOAP
Problem ze standardem SOAP jest taki, iż jest on w miarę skomplikowany i ciężki w użytkowniu. Oczywiście nie jest to rocket science, jednak budowanie zapytań SOAP (bazujące na XML) oraz parsowanie odpowiedzi wymaga bardziej złożonych narzędzi, takich jak:
- Biblioteka do XML
- Narzędzie generujące kod na podstawie WSDL
Na desktopach te problemy były jeszcze w miarę łatwe do rozwiązania. Większość języków i technologii posiada np biblioteki do parsowania XML, a także istnieją narzędzia do generowania kodu z WSDL.
W przypadku platform mobilnych sytuacja często jest o wiele gorsza. Niektóre języki nie posiadają bibliotek do XML, a te które takowe mają, to cierpią na brak narzędzi do generowania kodu.
Generatory kodu z WSDL
Już parę razy wspomniałem o narzędziach do generowania kodu, więc może czas wyjaśnić czym one są i dlaczego są takie istotne.
Zacznijmy jednak od tego, czym jest WSDL? Jest to plik opisujący strukturę zapytań i odpowiedzi danej usługi internetowej. Dzięki niemu wiemy jak stworzyć zapytanie, aby było poprawnie odebrane przez usługę oraz co dostaniemy w odpowiedzi. Jest to taki pradziadek Swaggera 😉
Teoretycznie mając plik WSDL programista mógłby zapytania tworzyć ręcznie, jednak jest to karkołomne zadanie, o wiele trudniejsze niż wysyłanie zapytań do API RESTowego. W związku z tym, powstały narzędzia, które analizują WSDLa i generują kod w różnych językach programowania, który może być użyty do komunikacji z usługą. W praktyce działa to bardzo dobrze. Np narzędzia takie jak Visual Studio mają taki generator wbudowany. W Javie też jest kilka profesjonalnych narzędzi (np. wsdl2Java), które sprawiają, że korzystanie z SOAP na desktopie jest proste.
W przypadku platform mobilnych, takich narzędzi jednak nie ma (lub są hobbistyczne narzędzia open source, które działają tylko w bardzo prostych przypadkach). Z tym problemem spotkałem się osobiście wiele lat temu, gdy tworzyłem aplikację BodyArchitect na system Android (więcej o niej możesz poczytać tutaj). Też miałem webserwis SOAP, z którym moja aplikacja mobilna miała „rozmawiać”. Okazało się wtedy, iż na platformach mobilnych nie jest to takie proste.
I tak pojawił się pomysł napisania własnego generatora kodu…
easyWSDL
easyWSDL jest aplikacją SaaS, która jest uniwersalnym generatorem kodu dla webserwisów SOAP. Jest rozwijana od roku 2014 i do tej pory skorzystało z niej kilkadziesiąt tysięcy użytkowników.
Obsługuje następujące języki programowania:
- Java (Android)
- Java (Pure)
- Kotlin (Android)
- Objective-C (iOS)
- Swift (iOS)
- Dart (Flutter)
Generator wspiera praktycznie wszystkie możliwe ficzery i konstrukcje, które mogą pojawić się w webserwisach SOAP (poza standardami WS-*, czyli WS-Security itp). Przez te wszystkie lata, za każdym razem gdy jakiś użytkownik znalazł błąd, to był on naprawiany i implementowany dla każdego powyższego języka. A generator jest sprawdzony w boju przez takie firmy jak: Dolby, Honeywell, Orange, Bosh i wiele innych.
Jak korzystać z easyWSDL?
Na początku warto wspomnieć, iż korzystanie z easyWSDL jest płatne. Sam generator posiada wersję darmową, ale ma ona następujące ograniczenia:
- Wygenerowany kod nie implementuje zaawansowanych ficzerów
- Wygenerowany kod implementuje tylko połowę dostępnych metod. Druga połowa jest niezaimplementowana
- Kod wygenerowany wersją bezpłatną nie może być wykorzystany w celach komercyjnych
Podsumowując, wersja bezpłatna służy do tego, aby sprawdzić, czy generator będzie działał z konkretnym webserwisem.
Korzystać z easyWSDL można na dwa sposoby:
- Przez stronę https://easywsdl.com – ten sposób działa dla wszystkich języków
- Za pośrednictwem pluginu do IDE Android Studio lub IntelliJ – z tego sposobu mogą skorzystać tylko programiści używający jednego z tych środowisk
Na potrzeby tego artykułu pokaże jak wykorzystać easyWSDL w projekcie aplikacji mobilnej na system Android (Kotlin) przez stronę internetową. Jeśli piszesz aplikację w innym języku (np Swift na iOS) to nadal wiele z tego co przedstawię w tym artykule będzie aktualne.
Nasza przykładowa aplikacja będzie bardzo prosta. Głownym elementem jest przycisk, który po kliknięciu połączy się z API system PayPal, aby pobrać aktualny stan konta. PayPal posiada dwa różne API:
- REST – jest to standardowe API, które umożliwia pobieranie danych związanych z płatnościami, fakturami, zamówieniami itp. Niestety nie obsługuje wielu funkcji, jak np. możliwość pobrania salda konta itp.
- NVP/SOAP – jest to API, które nas interesuje, gdyż ono między innymi umożliwia pobranie stanu konta PayPal. Wykorzystuje standard SOAP, co sprawia, że na mobilkach ciężko użyć. I tutaj cały na biało wchodzi easyWSDL…
Jak korzystać PayPal NVP/SOAP API?
Zanim będziemy mogli skorzystać z API systemu PayPal, należy założyć konto. Możesz to zrobić tutaj.
W celu ułatwienia developmentu, API PayPala udostępnia tryb piaskownicy, który pozwala nam na bezpieczne eksperymentowanie. Dostęp do trybu piaskownicy oraz wiele innych narzędzi pomocych podczas korzystania z API PayPal znajdziesz na stronie https://developer.paypal.com/home/.
Zanim będziemy mogli skorzystać z API, należy dostać dane autoryzujące. W odróżnieniu od REST API, (których dane do autoryzujące tworzymy na stronie https://developer.paypal.com/), to w przypadku API NVP/SOAP należy:
- Zalogować się na swoje konto PayPal i przejść do ustawień konta
- Przechodzimy do sekcji Dostęp do API
- A następnie klikamy Zarządzaj danymi uwierzytelniającymi API w Integracja interfejsu API NVP/SOAP (wersja klasyczna)
- Tutaj znajdziesz trzy wartości, które sobie zapisz, bo będziesz potrzebował ich w kodzie:
- Nazwa użytkownika API
- Hasło API
- Podpis
W związku z tym, iż ten artykuł nie skupia się na korzystaniu z API PayPal, a na generatorze easyWSDL, to powyższe kroki są tylko skrótowym opisem. Jeśli chciałbyś dokładnie zapoznać się z API PayPala to odpowiednie informacje znajdziesz tutaj https://developer.paypal.com/home/.
Przykładowa aplikacja
Jak wspomniałem, w tym artykule skupimy się na generatorze easyWSDL oraz korzystaniu z webserwisów SOAP, a nie na tworzeniu aplikacji mobilnej na Androida. W związku z tym nasz projekt to głównie kod wygenerowany z szablonu Android Studio:
- Po uruchomieniu środowiska Android Studio wybieramy opcję File->New->New Project…
- Jako typ aplikacji wybieramy Phone and Tablet -> Basic Activity. Wygeneruje nam to aplikację z jedym przyciskiem, pod którym umieścimy kod do połączenia z webserwisem.
- Podajemy nazwę aplikacji oraz wybieramy Kotlin jako język programowania. Zwróć uwagę na pole Package name. Wartość z tego pola będziemy w dalszych krokach podawać w generatorze easyWSDL. Nasza testowa aplikacja ma nazwę pakietu
com.example.paypaltester
. - Klikamy Finish.
W tym momencie mamy działającą aplikację. Teraz czas wygenerować kod, który umożliwi nam połączenie się z usługą internetową PayPala. Na początek musimy zdobyć plik WSDL do naszego API. Po małych poszukiwaniach, znalazłem właściwy URL na tej stronie: https://developer.paypal.com/api/nvp-soap/PayPalSOAPAPIArchitecture/.
Nasz plik WSDL jest tutaj: https://www.paypal.com/wsdl/PayPalSvc.wsdl.
Czas przejść do easyWSDL…
Generowanie kodu w easyWSDL
Rozbijmy tą operację na poszczególne kroki:
- Zakładamy konto w serwisie https://easywsdl.com.
- Klikamy link Generate, co spowoduje przejście do strony generatora.
- W tym miejscu możemy skonfigurować nasz generator. Większość ustawień można zostawić bez zmian. Najważniejsze opcje, które możemy/musimy zmienić:
- WSDL Location – adres pliku WSDL naszego webservisu. W naszym przykładzie wprowadzamy wartość https://www.paypal.com/wsdl/PayPalSvc.wsdl.
- Platform – język programowania, w którym chcemy wygenerować kod. U nas będzie to Kotlin/Android.
- Package name – klasy w języku Kotlin lub Java zwykle są umieszczane w pakiecie. W tym miejscu możemy podać nazwę pakietu dla wygenerowanych klas. W naszym przykładzie będzie to
com.example.paypaltester.wsdl
. Zwróć uwagę, że do nazwy pakietu naszego przykładowego projektu dodałem końcówkę wsdl, co wyjaśniej w dalszej części wpisu.
Są to podstawowe opcje, które ustawiamy za każdym razem, gdy korzystamy z easyWSDL. Są też inne, które możemy zmienić: - Add MTOM transfer – MTOM transfer służy do wydajnego przesyłania dużych plików binarnych z/do webservisu. Jeśli masz taki przypadek to zaznacz tą opcję. Pamietaj jednak, że Twój webserwis musi obsługiwać transfer MTOM.
- Detect dictionaries – zaznaczenie spowoduje, że generator wykryje w pliku WSDL klasy słowników (np Hashmap, Map etc) i zamiast standardowo wygenerowanych klas, użyje wbudowanych w Kotlin (Javę) klas słowników.
- Implement Parcerable – zaznacz, jeśli chcesz aby wygenerowane klasy implementowały interface
Parcerable
- Implement Serializable – jak wyżej, z tym że chodzi o interface
Serializable
- Import documentation from wsdl – w plikach wsdl mogą znajdować się opisy (dokumentacja) typów używanych przez dany webserwis. Zaznacz tą opcję, aby zaimportować je do wygenerowanego kodu w postaci komentarzy.
- Gdy ustawiliśmy wszystkie opcje, czas kliknąć przycisk Generate. Po dłuższej chwili powinniśmy zobaczyć informację o zakończeniu generowania, a sam plik zip z kodem zacznie się automatycznie ściągać.
- Rozpakujmy plik zip i zobaczmy co jest w środku:
- ReadMe.txt – zawiera najważniejsze informacje wraz z instrukcją użycia wygenerowanego kodu
- docs – dokumentacja w formacie HTML wygenerowanego kodu
- libs – zewnętrzne biblioteki, które należy dołączyć do projektu, aby kod się kompilował
- src – najważniejsza część, czyli wygenerowany kod, służący do łączenia się z naszym API
Korzystanie z wygenerowanego kodu
Teraz nadszedł czas, aby wygenerowany kod wykorzystać w naszym testowym projekcie. Przejdzmy zatem do Android Studio. Dobrą praktyką jest wydzielenie wygenerowanego kodu od reszty projektu, w związku z czym stworzyłem folder wsdl, do którego skopiuję całą zawartość katalogu src (dlatego właśnie podczas generowania do nazwy pakietu dodałem wsdl).
Następnie należy dołączyć do naszego projektu wszystkie pliki jar znajdujące się w katalogu libs (w zależności od wybranych opcji, może nie być żadnych plików jar). W związku z tym, iż w generatorze wybrałem opcję Add MTOM transfer, to mam dwa pliki:
- apache-mime4j-core-0.8.9.jar
- apache-mime4j-dom-0.8.9.jar
Kopiuję je zatem do katalogu libs w naszym projekcie wraz z odpowiednimi wpisami w pliku build.gradle
android {
//added this section because sometimes there are conflicts during build phase
packagingOptions {
exclude 'META-INF/AL2.0'
exclude 'META-INF/LGPL2.1'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation files('libs/apache-mime4j-core-0.8.9.jar', 'libs/apache-mime4j-dom-0.8.9.jar') //<-easyWSDL dependencies
}
Czasem podczas kompilacji gradle zgłasza następujący błąd:
2 files found with path 'META-INF/DEPENDENCIES'.
Mój sposób, aby rozwiązać ten problem jest dodanie sekcji packagingOptions. Jeśli znasz czytelniku lepszy sposób, to daj znać w komentarzach.
W tym momencie kod powinien się poprawnie skompilować.
Następnym krokiem jest dodanie uprawnienia do łączenia się z Internetem. W tym celu dodajemy linijkę:
<uses-permission android:name="android.permission.INTERNET" />
w pliku AndroidManifest.xml.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.PayPalTester"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.PayPalTester.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Pozostaje nam dodanie kodu, który korzystając z klas wygenerowanych przez easyWSDL, połączy się z PayPalem i pobierze stan naszego konta.
Struktura wygenerowanego kodu easyWSDL
W zależności od wielkości webserwisu, wygenerowanych klas może być nawet kilkaset, dlatego skąd mamy wiedzieć od czego zacząć?
Wygenerowane klasy możemy podzielić na:
- Dane – są to wszystkie klasy, które służą do przesyłania danych z usługi internetowej. W tej kategorii mieszczą się wygenerowane enumy, kolekcje itp. Znajdziemy je jako parametry metod oraz to co one zwracają.
- Klasy serwisów – jedna lub więcej klas reprezentujących webserwis. Te klasy nas najbardziej interesują, gdyż od nich zaczynamy programowanie
- Klasy pomocnicze – wykorzystywane wewnętrznie przez easyWSDL. Zwykle nas nie interesują.
Z powyższego opisu wynika, że spośród tych wszystkich wygenerowanych klas musimy odnaleźć klasy serwisów. Możemy to zrobić na dwa sposoby:
- Na stronie easyWSDL po wygenerowaniu klas
Na poniższym obrazku pokazałem, gdzie znajduje się informacja o klasach serwisowych. Wystarczy skopiować nazwę takiej klasy i wyszukać ją wsród wygenerowanych plików
- W pliku ReadMe.txt
W wygenerowanym pliku zip znajduje się plik ReadMe.txt, w którym znajduje się wiele praktycznych informacji, łącznie z instrukcją jak korzystać z kodu. Jeden z punktów zawiera pseudokod w stylu:
val service = PayPalAPISoapBinding()
service.MethodToInvoke(...)
W tym przypadku PayPalAPISoapBinding jest nazwą klasy serwisowej, która nas interesuje.
Wywołanie metody GetBalance za pośrednictwem easyWSDL
Pozostaje nam teraz po prostu użyć klasy serwisowej i wywołać wybraną metodę API. W naszym przypadku chcemy pobrać stan konta PayPal:
val service= PayPalAPISoapBinding("https://api-3t.paypal.com/2.0/")
val request= GetBalanceRequestType()
val credentials= CustomSecurityHeaderType()
credentials.Credentials= UserIdPasswordType()
credentials.Credentials!!.Username="podaj swojego użytkownika API"
credentials.Credentials!!.Password="podaj hasło "
credentials.Credentials!!.Signature="podaj podpis"
request.Version="200"
request.ReturnAllCurrencies="1"
val res=service.GetBalance(request,credentials)
var str=""
for (bal in res!!.GetBalanceResponse_1!!.BalanceHoldings)
{
str=str+bal.currencyID+" = "+bal.value+"\r\n"
}
Po wygenerowaniu klas z PayPala, domyślnie łączą się one z kontami sandboxowymi. Aby nasz kod łączył się z produkcyjnym serwerem, należy ustawić odpowiedni URL (tak jak zrobiłem to powyżej). Listę dostępnych endpointów PayPala znajdziesz tutaj.
Zwróć uwagę, że w powyższym kodzie należy podać dane autoryzujące, które stworzyliśmy wcześniej. Dodatkowo należy podać wersję (nie ma ona dużego znaczenia, 200 działa dobrze 😉 ). Parametr ReturnAllCurrencies informuje PayPal, aby zwrócił saldo dla wszystkich walut w portfelu.
Powyższy kod wywoływany jest po kliknięciu w przycisk. W związku z tym, iż operacje sieciowe nie mogą być wykonywane w wątku głównym aplikacji, wykorzystałem lifecycleScope:
lifecycleScope.launch(Dispatchers.IO) {
//kod pobierający saldo z PayPal
}
Tak wygląda nasza aplikacja z wyświetlonym stanem konta PayPal.
Podsumowanie
Jak widać w naszym przykładzie, korzystanie z API SOAP na platformach mobilnych może być banalnym zadaniem, gdy skorzystamy z narzędzi do generowania, takich jak https://easywsdl.com. W przypadku chęci samodzielnego zaimplementowania całej komunikacji, byłoby to zadanie bardzo czasochłonne.
Mimo iż nasz przykład bazował na języku Kotlin i platformie Android, to w bardzo podobny (i prosty) sposób, korzystamy z easyWSDL dla pozostałych języków i platform (np. Swift, Flutter, itp).
Jeśli masz dodatkowe pytania lub pomysły na następne artykuły związane z generatorem easyWSDL, daj znać w komentarzach.
Webserwis SOAP w aplikacji Android z wykorzystaniem generatora easyWSDL
Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl