Merhabalar. Bugün SOAP bir servisle, Python istemcisi üzerinden nasıl konuşabileceğimize göz atacağız. Ama öncelikle biraz bilgi tazeleyelim;
API’lar (Application Programming Interface) en basit tabir ile bir uygulamanın yeteneklerinin, başka bir uygulama tarafından kullanılmasına, 2 uygulamanın birbirleriyle iletişimine olanak veren yapılardır.
Günümüzde, web servisleri olarak ifade edilen iki tip yaygın olarak kullanılmakta olup. REST ve SOAP olarak adlandırılmaktadırlar.
Bu yaklaşımlar, farklı kullanım şekillerine sahip olup, birbirlerine karşı avantaj ve dezavantajlar barındırsa da bir karşılaştırma yazısı olmadığından sadece SOAP ve Python istemcisi hazırlama üzerine konuşuyor olacağız.
SOAP nedir?
SOAP (Simple Object Access Protocol), uygulama yeteneklerinin farklı uygulamalar üzerinden kullanılması ve uygulamalar arası bilgi alışverişi sırasında kullanılan bir protokoldür. SOAP veri alışverişi sırasında veri formatı olarak XML kullanır. Genelde HTTP (Hyper Text Transfer Protocol) üzerinden veri alışverisi sağlansa da TCP/IP ile de gönderim yapılabilir.
Örnek bir soap isteği aşağıdaki gibidir;
$ curl --location --request POST 'http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso' \ --header 'Content-Type: text/xml; charset=utf-8' \ --data-raw '<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <CountryCurrency xmlns="http://www.oorsprong.org/websamples.countryinfo"> <sCountryISOCode>US</sCountryISOCode> </CountryCurrency> </soap:Body> </soap:Envelope>'
İstek gövdesi;
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <CountryCurrency xmlns="http://www.oorsprong.org/websamples.countryinfo"> <sCountryISOCode>US</sCountryISOCode> </CountryCurrency> </soap:Body> </soap:Envelope>
Cevap;
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <m:CountryCurrencyResponse xmlns:m="http://www.oorsprong.org/websamples.countryinfo"> <m:CountryCurrencyResult> <m:sISOCode>USD</m:sISOCode> <m:sName>Dollars</m:sName> </m:CountryCurrencyResult> </m:CountryCurrencyResponse> </soap:Body> </soap:Envelope>
XML yazım kuralları ve hakkında daha fazla bilgi almak isterseniz.
Python ile SOAP istekleri yapmak.
Öncelikle kendimize public kullanıma açık bir SOAP servis bulalım.
Buradaki adresten görülebileceği üzere, Postman’in kullanıma sunduğu bir çok public servis bulunuyor. Bizde CountryInfo servisini kullanarak ülke kodlarına göre başkenti döndüren basit bir uygulama yazacağız.
İstek cevap döngüsü kısaca aşağıdaki gibi olacak;
İstek ->
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <CapitalCity xmlns="http://www.oorsprong.org/websamples.countryinfo"> <sCountryISOCode>US</sCountryISOCode> </CapitalCity> </soap:Body> </soap:Envelope>
Cevap ->
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Body> <m:CapitalCityResponse xmlns:m="http://www.oorsprong.org/websamples.countryinfo"> <m:CapitalCityResult>Washington</m:CapitalCityResult> </m:CapitalCityResponse> </soap:Body> </soap:Envelope>
Python ile XML üzerinde çalışırken, işleri fazlasıyla kolaylaştıran xmltodict kütüphanesini yükleyerek devam edelim. xmltodict 2 basit fonksiyon ile Python dictionarylerini XML çıktılarına, XML elementleri içeren stringleri kolayca Python dictionarylerine çevirebiliyor.
pip install xmltodict
Oluşturacağınız dictionarylerin, key:value derinlik seviyelerine göre XML otomatik oluşturulacaktır.
Dictionaryden XML output oluşturmak;
import xmltodict sample_data = { "XmlBody": { "@element_attr" : "attribute_value", "element1": {"#text": "element 1 text."}, "element2": {"#text": "element 2 text."}, } } converted_xml = xmltodict.unparse(sample_data, pretty=True) print(converted_xml)
<?xml version="1.0" encoding="utf-8"?> <XmlBody element_attr="attribute_value"> <element1>element 1 text.</element1> <element2>element 2 text.</element2> </XmlBody>
Bir elemetin içinde bulundurduğu değeri #text keyi ile, element attributlarını ise varsayılan olarak “@attr” : “value” şeklinde belirleyebilmekteyiz. Bu değerler varsayılan olup; element değer keyini cdata_key, attribute prefixini attr_prefix üzerinden değiştirebiliriz. Örn;
tiny_data = {"xmlBody": {"*attr": "attribute_value", "#value": "element value."}} converted_xml = xmltodict.unparse( tiny_data, pretty=True, cdata_key="#value", attr_prefix="*" ) print(converted_xml)
<?xml version="1.0" encoding="utf-8"?> <xmlBody attr="attribute_value">element value.</xmlBody>
XML stringinden dictionary output oluşturmak;
import xmltodict xml_string = """<?xml version="1.0" encoding="utf-8"?> <XmlBody> <XmlResult> <ActualResult>Result is here.</ActualResult> </XmlResult> </XmlBody> """ converted_dict = xmltodict.parse(xml_string, encoding="utf-8") print(converted_dict) print(converted_dict["XmlBody"]["XmlResult"]["ActualResult"])
SOAP web servis isteği.
country_code = input("Country code? : ").upper() country_service_data = { "soap:Envelope": { "@xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/", "soap:Body": { "CapitalCity": { "@xmlns": "http://www.oorsprong.org/websamples.countryinfo", "sCountryISOCode": {"#text": country_code}, } }, } }
Kullanıcıdan ülke kodunu aldıktan sonra, yukarıda belirttiğimiz istek gövdesini Python dictionary kullanarak oluşturalım.
xml_dumped_data = xmltodict.unparse(input_dict=country_service_data, encoding="utf-8")
xmltodict yardımı ile XML dump alalım.
headers = {"Content-Type": "text/xml; charset=utf-8;"} url = "http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso"
Gerekli header ve url değişkenlerini tanımlayalım.
raw_resp = requests.post(url=url, data=xml_dumped_data, headers=headers)
İlgili servis POST metodu ile çalışmakta, o yüzden requests kütüphanesi yardımıyla POST isteğini çıkalım.
XML servisler genellikle status_code üzerinden bir hata bildirimi yapmamakta genellikle 200 ve 500 status code dönmekteler. (ya da benim çalıştıklarım hep öyleydi 🙂 )
Bu serviste yine aynı şekilde ülke kodunun bulunamaması durumunda, ilgilili mesajı yine body üzerinden göndermekte. O yüzden ülkenin bulunup bulunamamasını kontrol eden ve sysouta belirli durumları basan bir akış ekleyelim.
if raw_resp.status_code != 200: print("Service error.") exit() dumped_response = xmltodict.parse(raw_resp.content) result = dumped_response["soap:Envelope"]["soap:Body"]["m:CapitalCityResponse"][ "m:CapitalCityResult" ] if result == "Country not found in the database": print(f"No country matching the {country_code} country code was found.") exit() print(f"Country code {country_code} -> Capital City {result}")
Tamam görünüyor. Scripti çalıştıralım;
$ python script.py Country code? : tr Country code TR -> Capital City Ankara
$ python script.py Country code? : klm No country matching the KLM country code was found.
Sonuç.
Python ile basitçe SOAP bir servise nasıl istek gönderilip cevabın nasıl işlenecegi konusunda artık bilgi sahibiyiz.
Ben genellikle tercih etmesemde, doğrudan SOAP servisi discover eden ve metod çağrımı üzerinden çalışan Python paketleri de mevcut. Örneğin zeep. Bu istemciye de göz atmanızı öneririm.
Yazılan scriptin tamamı ise bu adreste.
Herkese iyi günler!
Zeep çok iyi bir proje ama SOAP servislerinin en temelde nasıl çalıştığını anlama açısından yazını başarılı ve değerli buluyorum. Tebrikler.