İçerik: Android üzerinden webservislere erişim, gelen yanıtın parse edilmesi ve kullanılması.
Bu seferki yazım bir yıldan fazla zamandır sıkça kullandığım, Android üzerinden SOAP [0] webservislerine bağlanma ve kullanma konusunda kolaylıklar sağlayan ksoap2 [1] hakkında olacak. E-mail doğrulaması yapan public bir webservisi kullanan örnek bir uygulama üzerinden gideceğim.
Kullanacağım webservisi basit göründüğü için aşağıdaki olarak seçtim (google’da karşıma ilk çıkanlardan biri):
http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx?op=VerifyEmail
Metodların tam listesi:
http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx
Ve servis tanımlaması:
http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx?WSDL
Kullanacağımız metoddan bahsedecek olursak, bir email ve lisans anahtarını parametre olarak alıyor, arka planda kontrolünü yapıyor ve işlem hakkında 4 farklı bilgiyi bize veriyor (ResponseText, ResponseCode, LastMailServer, GoodEmail). Yapmak istediğim uygulama da verdiğim email ve lisans anahtarı bilgilerini bu servise gönderen ve gelen yanıtı aldıktan sonra parse edip, her bir bilgiyi ekranda ayrı ayrı gösterecek.
Servisi test etmek için url’de bir arayüz var ama böyle bir arayüze sahip olmayan servisler için test aracı olarak soapUI [2] ya da SOAPSonar [3] kullanılabilir. SOAPSonar Personal Edition’da test ettiğimde gönderilen istek aşağıdaki gibi oldu:
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws.cdyne.com/"> <soap:Body> <tns:VerifyEmail> <tns:email>oguzozkeroglu@hotmail.com</tns:email> <tns:LicenseKey>test</tns:LicenseKey> </tns:VerifyEmail> </soap:Body> </soap:Envelope>
Aldığım yanıt ise aşağıdaki gibi:
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <VerifyEmailResponse xmlns="http://ws.cdyne.com/"> <VerifyEmailResult> <ResponseText>Verified Email Address</ResponseText> <ResponseCode>2</ResponseCode> <LastMailServer>mx2.hotmail.com</LastMailServer> <GoodEmail>true</GoodEmail> </VerifyEmailResult> </VerifyEmailResponse> </soap:Body> </soap:Envelope>
Kullanmadığım mail adresimin verified olmasını görmek sevindirdi açıkçası :) Şimdi aynı işlemleri bir Android projesi ile yapalım ve gelen yanıtı anlamlı hale getirip XML taglari arasındaki bilgileri birbirinden ayırıp ekranda gösterelim.
Öncelikle Eclipse’te bir Android projesi yaratalım. Kullanacağımız hiçbir metod ve özellik android-4 üzeri bir sürüm gerektirmiyor, o yüzden minimum ve target sdk seçenekleri 1.6 tutulabilir (en yenisi olsun diye kasmaya gerek yok :)
Yapacağımız ilk iş Manifest dosyasına internete erişim iznini eklemek olursa sonradan gerçekleşmesi muhtemel bir hatayı (java.net.SocketException) en baştan önlemiş oluruz. Eğer uygulamamız internet ile iletişime geçecekse aşağıdaki izni Manifest dosyamıza eklemiş olmamız gerekiyor.
<uses-permission android:name="android.permission.INTERNET" />
Şimdi de gelen yanıttaki bilgileri tutacağımız nesne için bir class yazalım. Gelen XML’deki ada göre bir isimlendirme yaptım, o kısım için daha kolay akılda kalacak ya da daha anlamlı bir isim seçilebilir.
public class VerifyEmailResult { private String responseText; private int responseCode; private String lastMailServer; private boolean goodEmail; public VerifyEmailResult() { super(); } public VerifyEmailResult(String responseText, int responseCode, String lastMailServer, boolean goodEmail) { super(); this.responseText = responseText; this.responseCode = responseCode; this.lastMailServer = lastMailServer; this.goodEmail = goodEmail; } public String getResponseText() { return responseText; } public void setResponseText(String responseText) { this.responseText = responseText; } public int getResponseCode() { return responseCode; } public void setResponseCode(int responseCode) { this.responseCode = responseCode; } public String getLastMailServer() { return lastMailServer; } public void setLastMailServer(String lastMailServer) { this.lastMailServer = lastMailServer; } public boolean isGoodEmail() { return goodEmail; } public void setGoodEmail(boolean goodEmail) { this.goodEmail = goodEmail; } @Override public String toString() { return "VerifyEmailResult [responseText=" + responseText + ", responseCode=" + responseCode + ", lastMailServer=" + lastMailServer + ", goodEmail=" + goodEmail + "]"; } }
Daha sonra bu classtan oluşturduğumuz nesnenin içini dolduracak, aslında yapmamız gereken işin çoğunu yapacak (webservis ile bağlantı kurup, parametrelerimizi karşıya gönderecek, ordan yanıt bekleyip aldığı yanıtı parse edip sonrasında bilgileri istediğimiz formatta bize verecek) olan classı yazalım.
import org.ksoap2.SoapEnvelope; import org.ksoap2.serialization.SoapObject; import org.ksoap2.serialization.SoapSerializationEnvelope; import org.ksoap2.transport.HttpTransportSE; public class EmailValidator { private static final String METHOD_NAME = "VerifyEmail"; private static final String NAMESPACE = "http://ws.cdyne.com/"; private static final String SOAP_ACTION = "http://ws.cdyne.com/VerifyEmail"; private static final String URL = "http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx"; public static VerifyEmailResult verifyEmail (String eMail, String licenseKey) { VerifyEmailResult object = new VerifyEmailResult(); SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME); request.addProperty("email", eMail); request.addProperty("LicenseKey", licenseKey); SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.dotNet = true; envelope.setOutputSoapObject(request); HttpTransportSE androidHttpTransport = new HttpTransportSE(URL); androidHttpTransport.debug = true; try { androidHttpTransport.call(SOAP_ACTION, envelope); SoapObject response = (SoapObject) envelope.getResponse(); if (response.hasProperty("ResponseText")) { if (response.getPropertyAsString("ResponseText") == null) { object.setResponseText(null); } else { object.setResponseText(response.getPropertyAsString("ResponseText")); } } if (response.hasProperty("ResponseCode")) { if (response.getPropertyAsString("ResponseCode") == null) { object.setResponseCode(-1); } else { object.setResponseCode(Integer.parseInt(response.getPropertyAsString("ResponseCode"))); } } if (response.hasProperty("LastMailServer")) { if (response.getPropertyAsString("LastMailServer") == null) { object.setLastMailServer(null); } else { object.setLastMailServer(response.getPropertyAsString("LastMailServer")); } } if (response.hasProperty("GoodEmail")) { if (response.getPropertyAsString("GoodEmail") == null) { object.setGoodEmail(false); } else { object.setGoodEmail(Boolean.parseBoolean(response.getPropertyAsString("GoodEmail"))); } } } catch (Exception e) { e.printStackTrace(); } return object; } }
Bu classı yazdığımızda IDE org.ksoap2.* importlarını yapamadığı için uyarı verecektir çünkü standart Java ve Android SDK içinde o classlar bulunmamaktadır. Peki şimdi ne yapmak gerekir? Yazının konusu olan ksoap2 kütüphanesini indirip [4] projemize eklemeliyiz. Benim indirip kullandığım dosyanın ismi: ksoap2-android-assembly-2.6.0-jar-with-dependencies.jar. JAR dosyasını projeme eklemek için de kullandığım yöntem: Proje klasörü içinde libs isminde bir klasör oluşturup projeye eklemek istediğim jar dosyalarını oraya attıktan sonra Eclipse Package Explorer’da proje üzerinde sağ click -> Properties -> Java Build Path -> Libraries -> Add JARs -> sonrasında da klasörün içindeki istenilen JAR dosyalarını eklenmesi..
Bu işlemler sıkıntısız şekilde tamamlandıktan sonra projede herhangi bir hata kalmaması gerekiyor.
Şimdi yukardaki kodda ne yaptığımıza gelelim.
private static final String METHOD_NAME = "VerifyEmail"; private static final String NAMESPACE = "http://ws.cdyne.com/"; private static final String SOAP_ACTION = "http://ws.cdyne.com/VerifyEmail"; private static final String URL = "http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx";
Burda metod ismi, namespace, soap action ve servisin bulunduğu adresi belirttik.
VerifyEmailResult object = new VerifyEmailResult();
Parse edilen verileri tutacağımız geçici nesnemizi yarattık.
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME); request.addProperty("email", eMail); request.addProperty("LicenseKey", licenseKey); SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.dotNet = true; envelope.setOutputSoapObject(request); HttpTransportSE androidHttpTransport = new HttpTransportSE(URL); androidHttpTransport.debug = true;
Burda da servise yapacağımız isteği oluşturup parametreleri ekliyoruz. Sonrasında SOAP envelope nesnesini oluşturup, bir .net servisini çağıracağımızı belirtiyoruz.
androidHttpTransport.call(SOAP_ACTION, envelope); SoapObject response = (SoapObject) envelope.getResponse();
Burda servisi çağırıp gelen yanıtı SoapObject türünden bir nesneye atıyoruz.
XML’den farklı olarak ksoap2 nesneleri kendine özel bir formatta tutulur ve bunlar üzerinde işlem yapılır. Örneğin yukarıda XML halini yazmış olduğum isteğe karşılık gelen bizim request nesnemizin içeriği aşağıdaki gibi olacaktır:
VerifyEmail{ email=oguzozkeroglu@hotmail.com; LicenseKey=test; }
Gelen yanıta karşılık gelen response nesnemizin içeriği de aşağıdaki gibi olacaktır:
anyType{ ResponseText=Verified Email Address; ResponseCode=2; LastMailServer=mx4.hotmail.com; GoodEmail=true; }
Bu format parse işlemlerinde kolaylık sağlıyor. anyType XML’deki root’a yani VerifyEmailResult‘a karşılık geliyor gibi düşünebiliriz. Diğerleri zaten isminden anlaşılıyor. Herhangi bir özelliğe erişmek için
response.getPropertyAsString("OzellikIsmi");
kullanılabilir. (response.getProperty() ise geri bir String değil SoapObject döndüreceği için ekranda görüntülemede ya da nesnenin özelliklerini atamada sıkıntı çıkarabilir. Özelliğe o nesneden ulaşmak istediğimizde ise aşağıdaki gibi bir yapı kullanabiliriz.
String.valueOf(response.getProperty("OzellikIsmi"));
ksoap2’nin 2.5.8 sürümüne kadar böyle erişiliyordu. Parse işlemi için response nesnemizin öyle bir özelliği varsa ve null değilse kontrollerinden sonra atama yapıp en son nesneyi geri döndürüyoruz.
if (response.hasProperty("ResponseText")) { if (response.getPropertyAsString("ResponseText") == null) { object.setResponseText(null); } else { object.setResponseText(response.getPropertyAsString("ResponseText")); } } /** ... */
Bu iki classı yazdıktan sonra ana sayfaya ait olan layout sayfasına (XML), bir adet Button ve 4 adet TextView ekledim. İşimiz tasarımla değil arka plandaki işlerle olduğu için tasarıma pek takılmayalım, dandik durduğunun farkındayım :)
Artık elimizdeki malzemeleri kullanmanın zamanı geldi. Ana sayfada aşağıdaki global tanımlamaları ekledim
VerifyEmailResult object = null; final String eMail = "oguzozkeroglu@hotmail.com"; final String licenseKey = "test"; mThread thread = null;
Butonu tanımlayıp işlevsellik kazandıralım
Button btnInvokeService = (Button) findViewById(R.id.btnInvoke); btnInvokeService.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { thread = new mThread(); thread.start(); } });
Butona basındığında tanımladığımız thread çalışacak. Peki thread ne yapacak?
private class mThread extends Thread { @Override public void run() { object = EmailValidator.verifyEmail(eMail, licenseKey); handler.sendEmptyMessage(0); } private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { thread.interrupt(); setTexts(); } }; }
Çağırdığımızda EmailValidator classımızın verifyEmail metodunu çağıracak ve işlem bittikten sonra setTexts metodu çağrılacak. O metod ne yapacak?
private void setTexts() { TextView tv1 = (TextView) findViewById(R.id.tv1); TextView tv2 = (TextView) findViewById(R.id.tv2); TextView tv3 = (TextView) findViewById(R.id.tv3); TextView tv4 = (TextView) findViewById(R.id.tv4); tv1.setText("ResponseText ->" + object.getResponseText()); tv2.setText("ResponseCode ->" + object.getResponseCode()); tv3.setText("LastMailServer ->" + object.getLastMailServer()); tv4.setText("GoodEmail ->" + object.isGoodEmail()); }
Basit bir şekilde servisten dönen bilgileri oluşturduğumuz TextView‘lara atayacak ve ekranda görünmesini sağlayacak. Butona basıldıktan kısa bir süre sonra aşağıdakine benzer bir şeyler ortaya çıkmalı:
Orada görünen ResponseText ve ResponseCode listesinin tamamını metodlardan biri veriyor. Ben çıktıyı buraya da yazayım.
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <ReturnCodesResponse xmlns="http://ws.cdyne.com/"> <ReturnCodesResult> <anyType xsi:type="xsd:string">0-Invalid Email Address</anyType> <anyType xsi:type="xsd:string">1-Not Used</anyType> <anyType xsi:type="xsd:string">2-Verified Email Address (Treat as a good email)</anyType> <anyType xsi:type="xsd:string">3-Mail Server will accept email (Treat as a good email)</anyType> <anyType xsi:type="xsd:string">4-User not found</anyType> <anyType xsi:type="xsd:string">5-Email Domain not found</anyType> <anyType xsi:type="xsd:string">6-Not Used</anyType> <anyType xsi:type="xsd:string">7-SMTP/Timeout Error (Treat as a good email)</anyType> <anyType xsi:type="xsd:string">8-Domain valid. Email server(s) down</anyType> <anyType xsi:type="xsd:string">9-License Key allowance exceeded</anyType> </ReturnCodesResult> </ReturnCodesResponse> </soap:Body> </soap:Envelope>
Umarım yararlı bir yazı olmuştur. Kullandığım yöntemler bu işi yapmanın en iyi yöntemleri olmayabilir, araçlar ve kullandığım terimler doğru olmayabilir ama bu haliyle benim işimi gördüler bugüne kadar. Herhangi bir eleştiri / düzeltme / ekleme yapmak isteyen olursa konuya yorum bırakabilir ya da mail atabilir.
*0 -> http://en.wikipedia.org/wiki/SOAP
*1 -> http://code.google.com/p/ksoap2-android/
*2 -> http://www.soapui.org/
*3 -> http://www.crosschecknet.com/products/soapsonar.php
*4 -> http://code.google.com/p/ksoap2-android/wiki/HowToUse?tm=2
“Android :: Ksoap2 Kullanarak Webservislere Erişme” yazısına 32 yorum yapılmış.