10. Web Hizmetleri
10.1. Giriş
Bir önceki bölümde çeşitli TCP/IP istemci-sunucu uygulamalarını ele aldık. İstemciler ve sunucu arasında metin satırları alışverişi yapıldığı için, bu uygulamalar herhangi bir programlama dilinde yazılabilir. İstemcinin yalnızca sunucunun beklediği iletişim protokolünü bilmesi yeterlidir. Web hizmetleri, aşağıdaki özelliklere sahip TCP/IP sunucu uygulamalarıdır:
- Web sunucuları tarafından barındırılırlar ve bu nedenle istemci-sunucu iletişim protokolü, HTTP (HyperText Taşıma Protokolü) olup, bu protokol TCP-IP protokolünün üzerinde yer alır.
- Web hizmeti, sağlanan hizmet ne olursa olsun standart bir iletişim protokolüne sahiptir. Bir web hizmeti, S1, S2, .., Sn gibi çeşitli hizmetler sunar. Her biri, istemci tarafından sağlanan parametreleri bekler ve istemciye bir sonuç döndürür. Her hizmet için istemcinin şunları bilmesi gerekir:
- hizmetin tam adı, eğer
- hizmetin sağlanması için gerekli parametrelerin listesi ve türleri
- hizmet tarafından döndürülen sonucun türü
Bu unsurlar bilindiğinde, sorgulanan web hizmeti ne olursa olsun istemci-sunucu iletişimi aynı formatı izler. Böylece istemcilerin yazımı standart hale gelir.
- İnternetten gelen saldırılara karşı güvenlik nedeniyle, birçok kuruluş özel ağlara sahiptir ve sunucularının yalnızca belirli bağlantı noktalarını İnternet’e açar: esas olarak web hizmetinin 80 numaralı bağlantı noktası. Diğer tüm bağlantı noktaları kilitlidir. Bu nedenle, önceki bölümde anlatıldığı gibi istemci-sunucu uygulamaları özel ağ (intranet) içinde oluşturulur ve genellikle dışarıdan erişilemez. Bir hizmeti bir web sunucusunda barındırmak, onu tüm internet topluluğuna erişilebilir hale getirir.
- Web hizmeti, uzak bir nesne olarak modellenebilir. Sunulan hizmetler, bu nesnenin yöntemleri haline gelir. Bir istemci, bu uzak nesneye sanki yerelmiş gibi erişebilir. Bu, ağ iletişiminin tamamını gizler ve bu katmandan bağımsız bir istemci oluşturulmasına olanak tanır. Bu katman değişse bile, istemcinin değiştirilmesine gerek kalmaz. Bu, çok büyük bir avantajdır ve muhtemelen Web hizmetlerinin en önemli özelliğidir.
- Bir önceki bölümde ele alınan TCP/IP istemci-sunucu uygulamalarında olduğu gibi, istemci ve sunucu herhangi bir programlama dilinde yazılabilir. Aralarında metin satırları alışverişi yaparlar. Bu satırlar iki bölümden oluşur:
- HTTP protokolü için gerekli başlıklar
- mesajın gövdesi. Sunucudan istemciye gönderilen yanıt, XML (eXtensible İşaretleme Dili) biçimindedir. İstemciden sunucuya gönderilen bir istek için ise mesajın gövdesi, XML dahil olmak üzere çeşitli biçimlerde olabilir. İstemciden gelen XML isteği, SOAP (Simple Object Access Protocol) adı verilen özel bir biçime sahip olabilir. Bu durumda, sunucunun yanıtı da SOAP biçimini izler.
10.2. Tarayıcılar ve XML
Web hizmetleri, müşterilerine XML gönderir. Tarayıcılar, bu XML akışını aldıklarında farklı tepkiler verebilir. Internet Explorer, bunu görüntülemek için önceden tanımlanmış bir stil sayfasına sahiptir. Netscape Communicator’da ise bu stil sayfası bulunmadığından, alınan XML kodunu görüntülemez. XML’e erişmek için alınan sayfanın kaynak kodunu görüntülemek gerekir. Aşağıda, şu XML kodu için bir örnek verilmiştir:
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="st.istia.univ-angers.fr">bonjour de nouveau !</string>
Internet Explorer şu sayfayı görüntüleyecektir:

Netscape Navigator ise şunu gösterecektir:

Netscape tarafından alınan sayfanın kaynak koduna bakıldığında şunlar görülür:

Netscape, Internet Explorer ile aynı veriyi almıştır ancak bunu farklı şekilde görüntülemiştir. Bundan sonra ekran görüntüleri için Internet Explorer'ı kullanacağız.
10.3. İlk web hizmeti
Web hizmetlerini, üç farklı versiyonu olan çok basit bir örnek üzerinden inceleyeceğiz.
10.3.1. Sürüm 1
Bu ilk versiyon için, hemen kullanıma hazır bir web hizmeti iskeleti oluşturabilme avantajı sunan VS.NET'i kullanacağız. Bu mimariyi anladıktan sonra, kendi başımıza çalışmaya başlayabileceğiz. Bu, sonraki versiyonların konusu olacaktır.
VS.NET ile, [Fichier/Nouveau/Projet] seçeneğini kullanarak yeni bir proje oluşturalım:

Aşağıdaki noktalara dikkat edelim:
- proje türü Visual Basic'tir (sol çerçeve)
- proje şablonu ASP.NET Web Hizmeti'dir (sağdaki çerçeve)
- konum serbesttir. Burada, web hizmeti yerel bir IIS web sunucusu tarafından barındırılacaktır. Dolayısıyla, URL adresi http://localhost/[chemin] olacaktır; burada [chemin] değeri tanımlanmalıdır. Burada http://localhost/polyvbnet/demo yolunu seçiyoruz. VS.NET, bu proje için bir klasör oluşturacaktır. Nerede? IIS sunucusu, sunduğu web belgeleri ağacının bir kök dizinine sahiptir. Bu kök dizine <IISroot> diyelim. Bu, http://localhost adresindeki URL'e karşılık gelir. Buradan, URL http://localhost/polyvbnet/demo adresinin <IISroot>/polyvbnet/demo klasörüyle ilişkilendirileceği sonucuna varılır. <IISroot> normalde IIS'in kurulduğu sürücüdeki \inetpub\wwwroot klasörüdür. Örneğimizde bu, E sürücüsüdür. Dolayısıyla, VS.NET tarafından oluşturulan klasör, e:\inetpub\wwwroot\polyvbnet\demo klasörüdür:

Her zamanki gibi, çok fazla klasör oluşturulmuştur. Bunların hepsi her zaman gerekli değildir. Biz sadece belirli bir anda ihtiyacımız olanları açıklayacağız. VS.NET bir proje oluşturmuştur:

Projenin fiziksel klasöründe bulunan bazı dosyaları burada da görüyoruz. Bizim için en ilgi çekici olanı, asmx uzantılı dosyadır. Bu, web hizmetlerinin uzantısıdır. Bir web hizmeti, VS.NET tarafından bir Windows uygulaması gibi yönetilir; c.a.d gibi, bir grafik arayüze ve onu yönetmek için kod içeren bir uygulama. Bu nedenle, bir tasarım penceremiz var:

Bir web hizmeti normalde grafik kullanıcı arayüzüne sahip değildir. Uzaktan çağrılabilen bir nesneyi temsil eder. Yöntemlere sahiptir ve uygulamalar bu yöntemleri çağırır. Dolayısıyla onu, ağ üzerinden uzaktan örneklenebilme özelliğine sahip klasik bir nesne olarak ele alacağız. Bu nedenle, VS.NET tarafından sunulan tasarım penceresini kullanmayacağız. Bunun yerine, Görünüm/Kod seçeneğini kullanarak hizmetin koduna odaklanalım:

Dikkat edilmesi gereken birkaç nokta var:
- dosyanın adı Service1.asmx.vb'tir, Service1.asmx değildir. Service1.asmx dosyasının içeriğine biraz ileride tekrar değineceğiz.
- VS.NET ile Windows uygulamaları oluştururken karşılaştığımız kod penceresine benzer bir pencere karşımıza çıkıyor
VS.NET tarafından oluşturulan kod şöyledir:
Imports System.Web.Services
<System.Web.Services.WebService(Namespace := "http://tempuri.org/demo/Service1")> _
Public Class Service1
Inherits System.Web.Services.WebService
#Bölge "Web Hizmetleri Tasarımcısı tarafından oluşturulan kod"
Public Sub New()
MyBase.New()
'Bu çağrı, Web Hizmetleri Tasarımcısı tarafından zorunlu kılınmıştır.
InitializeComponent()
'InitializeComponent() çağrısından sonra başlatma kodunuzu ekleyin
End Sub
'Web Hizmetleri Tasarımcısı tarafından zorunlu kılınmıştır
Private components As System.ComponentModel.IContainer
'REMARQUE: Aşağıdaki prosedür, Web Hizmetleri Tasarımcısı tarafından zorunlu kılınmıştır
'Web Hizmetleri Tasarımcısı kullanılarak değiştirilebilir.
'Kod düzenleyicisini kullanarak bunu değiştirmeyin.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
components = New System.ComponentModel.Container()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
'CODEGEN: Bu prosedür, Web Hizmetleri Tasarımcısı tarafından zorunlu kılınmıştır
'Kod düzenleyicisini kullanarak bunu değiştirmeyin.
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
#Bölge Sonu
' EXEMPLE DE SERVICE WEB
' HelloWorld() hizmet örneği, "Hello World" dizesini döndürür.
' Oluşturmak için aşağıdaki satırları yorum satırına dönüştürmeyin, ardından projeyi kaydedin ve derleyin.
' Bu web hizmetini test etmek için, .asmx dosyasının başlangıç sayfası olduğundan emin olun
' olduğundan emin olun ve F5'e basın.
'
'<WebMethod()> Public Function HelloWorld() As String
' HelloWorld = "Hello World"
' End Function
End Class
Öncelikle, burada WebService sınıfından türetilen Service1 sınıfı olduğunu belirtelim:
Bu da bizi System.Web.Services ad alanını içe aktarmaya yönlendirir:
Imports System.Web.Services
Sınıf bildiriminin önünde bir derleme özniteliği bulunur:
<System.Web.Services.WebService(Namespace := "http://tempuri.org/demo/Service1")> _
Public Class Service1
Inherits System.Web.Services.WebService
System.Web.Services.WebService() özniteliği, takip eden sınıfın bir web hizmeti olduğunu belirtir. Bu öznitelik, NameSpace adlı parametre de dahil olmak üzere çeşitli parametreleri kabul eder. Bu parametre, web hizmetini bir ad alanına yerleştirmek için kullanılır. Nitekim, dünyada "meteo" adlı birden fazla web hizmeti olduğunu düşünebiliriz. Bunları birbirinden ayırt etmenin bir yoluna ihtiyacımız var. Ad alanı bunu mümkün kılar. Bir tanesi [espacenom1].meteo, diğeri ise [espacenom2].meteo olarak adlandırılabilir. Burada, sınıf ad alanlarına benzer bir kavramla karşılaşıyoruz. VS.NET, otomatik olarak kod oluşturmuş ve bunu kaynak kodun bir bölümüne yerleştirmiştir:
Bu koda bakıldığında, Windows uygulamaları geliştirilirken tasarımcının ürettiği kodla aynı olduğu görülür. Grafik kullanıcı arayüzü yoksa bu kodu tamamen silebiliriz; web hizmetleri için durumumuz da budur.
Ders, bir web hizmetinin nasıl olabileceğine dair bir örnekle sona eriyor:
#End Region
' EXEMPLE DE SERVICE WEB
' HelloWorld() hizmet örneği, "Hello World" dizesini döndürür.
' Oluşturmak için aşağıdaki satırları yorum satırına dönüştürmeyin, ardından projeyi kaydedin ve derleyin.
' Bu web hizmetini test etmek için, .asmx dosyasının başlangıç sayfası olduğundan emin olun
' olduğundan emin olun ve F5'e basın.
'
'<WebMethod()> Public Function HelloWorld() As String
' HelloWorld = "Hello World"
' End Function
Yukarıda anlatılanlara dayanarak, kodu temizleyip şu hale getiriyoruz:
Imports System.Web.Services
<System.Web.Services.WebService(Namespace:="st.istia.univ-angers.fr")> _
Public Class Bonjour
Inherits System.Web.Services.WebService
<WebMethod()> Public Function Bonjour() As String
Return "bonjour !"
End Function
End Class
Artık durumu biraz daha net görebiliyoruz.
- Bir web hizmeti, WebService sınıfından türetilen bir sınıftır
- Sınıf, <System.Web.Services.WebService(Namespace:="st.istia.univ-angers.fr")> özniteliği ile tanımlanmıştır. Dolayısıyla hizmetimizi st.istia.univ-angers.fr ad alanına yerleştiriyoruz.
- Sınıfın yöntemleri, ağ üzerinden uzaktan çağrılabilen bir yöntem olduğunu belirten <WebMethod()> özniteliği ile nitelendirilmiştir
Web hizmetimizi sağlayan sınıfın adı Bonjour'dur ve kendisi de Bonjour adında, bir karakter dizisi döndüren tek bir yönteme sahiptir. İlk test için hazırız.
- Henüz yapılmadıysa IIS web sunucusunu başlatalım
- Hata Ayıklama/Hata Ayıklamadan Çalıştır seçeneğini kullanalım. VS.NET
VS.NET, uygulamanın tamamını derleyecek, bir tarayıcıyı başlatacak (varsa genellikle Internet Explorer) ve http://localhost/polyvbnet/demo/Service1.asmx URL'sini görüntüleyecektir:

Neden http://localhost/polyvbnet/demo/Service1.asmx URL'si? Çünkü bu, projedeki tek .asmx dosyasıydı:

Birden fazla .asmx dosyası olsaydı, hangisinin ilk olarak çalıştırılacağını belirtmemiz gerekirdi. Bunu, ilgili .asmx dosyasına sağ tıklayıp [Définir comme page de démarrage] seçeneğini seçerek yapabilirsiniz.

service1.asmx dosyasının içeriğini merak ediyor olabiliriz. Nitekim, VS.NET dosyasıyla çalışırken service1.asmx.vb dosyası üzerinde çalıştık, service1.asmx dosyası üzerinde değil. Bu dosya proje klasöründe yer almaktadır:

Bir metin düzenleyiciyle (Not Defteri veya başka bir program) açalım. Şu içeriği elde ederiz:
Dosya, IIS sunucusuna yönelik,
- bir web hizmeti ile karşı karşıya olduğumuzu (anahtar kelime WebService)
- bu hizmetin sınıfının dilinin Visual Basic olduğu (Language="vb")
- bu sınıfın kaynak kodu Service1.asmx.vb dosyasında bulunur (Codebehind="Service1.asmx.vb")
- hizmeti uygulayan sınıfın adı demo.Bonjour'tir (Class="demo.Bonjour"). VS.NET'in Bonjour sınıfını, aynı zamanda projenin adı olan demo ad alanına yerleştirdiği dikkatinizi çekecektir.
http://localhost/polyvbnet/demo/Service1.asmx URL'sinden ulaşılan sayfaya geri dönelim:

Yukarıdaki sayfanın HTML kodunu kim yazdı? Biz yazmadık, bunu biliyoruz. Bu, web hizmetlerini standart bir şekilde sunan IIS'tir. Bu sayfa bize iki bağlantı sunuyor. İlkini, [Description du service]'i takip edelim:

Oops... bu oldukça anlaşılmaz bir XML. Yine de şuna dikkat edelim: URL
http://localhost/polyvbnet/demo/Service1.asmx?WSDL adresine dikkat edelim. Bir tarayıcı açın ve bu URL'yi doğrudan yazın. Daha önce gördüğümüzle aynı sonucu elde edersiniz. Dolayısıyla, http://serviceweb?WSDL URL'sinin web hizmetinin XML açıklamasına erişim sağladığını hatırlayalım. Başlangıç sayfasına geri dönelim ve [Bonjour] bağlantısını seçelim. Bonjour’un web hizmetinin bir yöntemi olduğunu hatırlayalım. Eğer birden fazla yöntem olsaydı, hepsi burada gösterilirdi. Aşağıdaki yeni sayfaya ulaşırız:

Örneğimizi gereksiz yere uzatmamak için elde ettiğimiz sayfayı kasıtlı olarak kısalttık. Elde ettiğimiz URL’ye tekrar dikkat edelim:
Bu URL'yi bir tarayıcıya doğrudan yazarsak, yukarıdakiyle aynı sonucu alırız. [Appeler] düğmesini kullanmamız isteniyor. Hadi bunu yapalım. Yeni bir sayfa elde ediyoruz:

Bu yine XML. Burada, web hizmetimizde bulunan iki bilgiyi görüyoruz:
- hizmetimizin st.istia.univ-angers.fr ad alanı
<System.Web.Services.WebService(Namespace:="st.istia.univ-angers.fr")>
- Bonjour yöntemi tarafından döndürülen değer:
Return "bonjour !"
Ne öğrendik?
- S web hizmetini yazma yöntemini
- Bu hizmeti nasıl çağıracağımızı
Şimdi VS.NET'in yardımı olmadan bir web hizmeti yazmaya odaklanacağız.
10.3.2. Sürüm 2
Önceki örnekte, VS.NET birçok işi tek başına halletti. Bu araç olmadan bir web hizmeti oluşturmak mümkün mü? Cevap evet ve şimdi bunu göstereceğiz. Bir metin düzenleyici kullanarak şu web hizmetini oluşturacağız:
Imports System.Web.Services
<System.Web.Services.WebService(Namespace:="st.istia.univ-angers.fr")> _
Public Class Bonjour2
Inherits System.Web.Services.WebService
<WebMethod()> Public Function getBonjour() As String
Return "bonjour de nouveau !"
End Function
End Class
Sınıfın adı Bonjour2 ve getBonjour adlı bir yöntemi var. Bu sınıf, demo2.vb dosyasına yerleştirilmiştir; bu dosya ise IIS sunucusunun dizin yapısı içinde, E:\Inetpub\wwwroot\polyvbnet\demo2 klasöründe bulunmaktadır. Bu, derlenebilen klasik bir VB.NET sınıfıdır:
dos>vbc /out:demo2 /t:library /r:system.dll /r:system.web.services.dll demo2.vb
dos>dir
02/03/2004 18:04 286 demo2.vb
02/03/2004 18:10 77 demo2.asmx
02/03/2004 18:12 3 072 demo2.dll
demo2.dll derlemesini bir bin klasörüne yerleştiriyoruz (bu isim zorunludur):
Şimdi demo2.asmx dosyasını oluşturuyoruz. Web istemcileri tarafından çağrılacak olan dosya budur. İçeriği şöyledir:
Bu yönergeyle daha önce karşılaşmıştık. Bu yönerge şunu belirtir:
- web hizmetinin sınıfının adı Bonjour2'dir ve demo2.dll derlemesinde bulunur. IIS, bu derlemeyi çeşitli yerlerde, özellikle de web hizmetinin bin klasöründe arayacaktır. Bu nedenle, demo2.dll derlemesini buraya yerleştirdik.
Şimdi çeşitli testler yapabiliriz. IIS'in etkin olduğundan emin olduktan sonra, bir tarayıcıyla http://localhost/polyvbnet/demo2/demo2.asmx adresine erişelim:

Ardından http://localhost/polyvbnet/demo2/demo2.asmx?WSDL adresini girin

Ardından URL http://localhost/polyvbnet/demo2/demo2.asmx?op=getBonjour, burada getBonjour, web hizmetimizin tek yönteminin adıdır:

Yukarıdaki [Appeler] düğmesini kullanıyoruz:

Web hizmetinin getBonjour yöntemine yapılan çağrının sonucunu başarıyla alıyoruz. Artık vs.net olmadan bir web hizmeti oluşturmayı biliyoruz. Bundan sonra, web hizmetinin nasıl oluşturulduğunu bir kenara bırakıp sadece temel dosyalara odaklanacağız.
10.3.3. Sürüm 3
[Bonjour] web hizmetinin önceki iki sürümü iki dosya kullanıyordu:
- web hizmetinin giriş noktası olan bir .asmx dosyası
- web hizmetinin kaynak kodu olan bir .vb dosyası
Burada, yalnızca .asmx dosyasıyla yetinebileceğimizi göstereceğiz. demo3.asmx hizmetinin kodu şöyledir:
Hizmetin kaynak kodunun artık doğrudan demo3.asmx dosyasının kaynak dosyasında yer aldığını görüyoruz. Şu yönerge:
yönergesi artık harici bir derlemede bulunan bir sınıfa değil, aynı kaynak dosyada bulunan bir sınıfa başvurmaktadır. Bunu <IISroot>\polyvbnet\demo3 klasörüne yerleştirelim:

IIS'i çalıştırıp http://localhost/polyvbnet/demo3/demo3.asmx URL'sini isteyelim:

Önceki sürüme kıyasla önemli bir fark görüyoruz: hizmetin VB kodunu derlememiz gerekmedi. IIS, aynı makineye kurulu olan VB.NET derleyicisi aracılığıyla bu derlemeyi kendisi gerçekleştirdi. Ardından sayfayı sundu. Derleme hatası olması durumunda, bu hata IIS tarafından bildirilir:

10.3.4. Sürüm 4
Burada IIS sunucusunun yapılandırmasıyla ilgileniyoruz. Şimdiye kadar web hizmetlerimizi her zaman IIS sunucusunun <IISroot> kök dizin yapısına yerleştirmiştik; burada ise [e:\inetpub\wwwroot]'e yerleştiriyoruz. Burada, web hizmetini istediğimiz herhangi bir yere yerleştirebileceğimizi gösteriyoruz. Bu, IIS'in sanal klasörleri kullanılarak yapılır. Hizmetimizi şu klasöre yerleştirelim:

[D:\data\devel\vbnet\poly\chap9\demo3] klasörü, IIS sunucusunun dizin yapısında bulunmamaktadır. Bunu, IIS adlı sanal bir klasör oluşturarak sunucuya belirtmeliyiz. IIS'i çalıştıralım ve aşağıdaki [Avancé] seçeneğini seçelim:

Karşımıza sanal dizinlerin bir listesi çıkıyor. Bu liste üzerinde fazla durmayacağız. Yukarıdaki [Ajouter] düğmesiyle yeni bir sanal dizin oluşturuyoruz:

[Parcourir] düğmesini kullanarak, web hizmetini içeren fiziksel klasörü belirleriz; burada bu klasör [D:\data\devel\vbnet\poly\chap9\demo3]'tir. Bu klasöre mantıksal (sanal) bir ad veriyoruz: [virdemo3]. Bu, [D:\data\devel\vbnet\poly\chap9\demo3] fiziksel klasörünün içindeki belgelere ağ üzerinden [http://<machine>/virdemo3] URL'si aracılığıyla erişilebileceği anlamına gelir. Yukarıdaki iletişim kutusunda başka ayarlar da bulunmaktadır; bunları olduğu gibi bırakıyoruz. İletişim kutusunu onaylıyoruz. Yeni sanal klasör, IIS'in sanal klasörler listesinde görünür:

Şimdi bir tarayıcı açıp [http://localhost/virdemo3/demo3.asmx] URL'sini giriyoruz. Daha önce gördüğümüzle aynı sonucu alıyoruz:

10.3.5. Sonuç
Bir web hizmeti oluşturmak için çeşitli yöntemler gösterdik. Bundan sonra, hizmeti oluşturmak için 3. yöntemi, hizmetin konumlandırılması için ise 4. yöntemi kullanacağız. Böylece VS.NET'e ihtiyacımız olmayacak. Bununla birlikte, hata ayıklamada sağladığı yardım nedeniyle VS.NET'i kullanmanın yararını da belirtelim. Web uygulamaları geliştirmek için ücretsiz araçlar mevcuttur; bunlardan biri, Microsoft tarafından desteklenen ve URL [http://www.asp.net/webmatrix] adresinde bulabileceğiniz WebMatrix ürünüdür. Bu, önceden herhangi bir yatırım yapmadan web programlamaya başlamak için mükemmel bir araçtır.
10.4. İşlemlere yönelik bir web hizmeti
Beş işlev sunan bir web hizmetini ele alalım:
- a+b sonucunu veren ekle(a,b)
- çıkarma(a,b) işlevi, a-b sonucunu verir
- çarpma(a,b) işlevi, a*b sonucunu verir
- böl(a,b) – a/b sonucunu verir
- her şeyi yapma(a,b) işlevi, [a+b, a-b, a*b, a/b] dizisini döndürür
Bu hizmetin VB.NET kodu şöyledir:
<%@ WebService language="VB" class=operations %>
imports system.web.services
<WebService(Namespace:="st.istia.univ-angers.fr")> _
Public Class operations
Inherits WebService
<WebMethod> _
Function ajouter(a As Double, b As Double) As Double
Return a + b
End Function
<WebMethod> _
Function soustraire(a As Double, b As Double) As Double
Return a - b
End Function
<WebMethod> _
Function multiplier(a As Double, b As Double) As Double
Return a * b
End Function
<WebMethod> _
Function diviser(a As Double, b As Double) As Double
Return a / b
End Function
<WebMethod> _
Function toutfaire(a As Double, b As Double) As Double()
Return New Double() {a + b, a - b, a * b, a / b}
End Function
End Class
Burada, daha önce verilmiş ancak tekrar hatırlatılması veya tamamlanması gereken bazı açıklamaları tekrarlıyoruz. operations sınıfı, VB.NET sınıfına benzer, ancak dikkat edilmesi gereken birkaç nokta vardır:
- Yöntemlerin başında, derleyiciye hangi yöntemlerin "yayınlanması" gerektiğini (WebMethod()) ve müşteriye sunulması gerektiğini belirten bir öznitelik bulunur. Bu özniteliğin önünde bulunmayan bir yöntem, uzak istemciler tarafından görülemez. Bu, diğer yöntemler tarafından kullanılan ancak yayınlanmak üzere tasarlanmamış bir iç yöntem olabilir.
- Sınıf, System.Web.Services ad alanında tanımlanan WebService sınıfından türetilmiştir. Bu miras alma her zaman zorunlu değildir. Özellikle bu örnekte, bu miras alma işlemi yapılmasa da olur.
- Sınıfın başında, web hizmetine bir ad alanı atamak amacıyla <WebService(Namespace="st.istia.univ-angers.fr")> özniteliği yer alır. Bir sınıf satıcısı, sınıflarına benzersiz bir ad vermek ve böylece aynı adı taşıyan diğer satıcıların sınıflarıyla çakışmaları önlemek amacıyla sınıflarına bir ad alanı atar. Web hizmetleri için de durum aynıdır. Her web hizmeti, benzersiz bir adla tanımlanabilmelidir; burada bu ad st.istia.univ-angers.fr'tir.
- Bir oluşturucu tanımlamadık. Dolayısıyla, üst sınıfın oluşturucusu örtük olarak kullanılacaktır.
Yukarıdaki kaynak kodu, doğrudan VB.NET derleyicisi için değil, IIS web sunucusu için tasarlanmıştır. Bu kodun sonuna .asmx uzantısı eklenmeli ve web sunucusunun dizin yapısına kaydedilmelidir. Burada dosyayı <IISroot>\polyvbnet\operations klasörüne operations.asmx adıyla kaydediyoruz:

Bu fiziksel klasöre, IIS [operations] adlı sanal klasörü eşliyoruz:
![]() | ![]() |
Bir tarayıcıyla hizmete erişelim. İstenen URl dosyası, [http://localhost/operations/operations.asmx]'tir:

operations web hizmetinde tanımlanan her bir yöntem için bir bağlantı içeren bir web belgesi elde ediyoruz. ajouter bağlantısını takip edelim:

Karşımıza çıkan sayfa, ajouter yöntemini, bu yöntemin ihtiyaç duyduğu a ve b argümanlarını sağlayarak test etmemizi öneriyor. ajouter yönteminin tanımını hatırlayalım:
Sayfanın, yöntem tanımında kullanılan a ve b argümanlarının adlarını aldığını not edelim. Appeler düğmesini kullandığımızda, tarayıcının ayrı bir penceresinde şu yanıtı alıyoruz:

Yukarıdaki örnekte [Affichage/Source] komutunu kullanırsak şu kodu elde ederiz:

Aynı işlemi [toutfaire] yöntemi için tekrar yapalım:

Şu sayfayı elde ediyoruz:

Yukarıdaki [Appeler] düğmesini kullanalım:

Her durumda, sunucunun yanıtı şu biçimdedir:
- yanıt, XML biçimindedir
- 1. satır standarttır ve yanıtta her zaman bulunur
- sonraki satırlar, sonuç türüne (double, ArrayOfDouble), sonuç sayısına ve web hizmetinin ad alanına (burada st.istia.univ-angers.fr) bağlıdır.
Bir web hizmetine sorgu göndermek ve yanıtını almak için çeşitli yöntemler vardır. Hizmetin URL yanıtına geri dönelim:

ve [ajouter] bağlantısını takip edelim. Görüntülenen sayfada, web hizmetinin [ajouter] işlevini sorgulamak için iki yöntem gösterilmektedir:



Bir web hizmetinin işlevlerine erişim sağlayan bu iki yöntem sırasıyla HTTP-POST ve SOAP olarak adlandırılır. Şimdi bunları sırayla inceleyeceğiz.
Not: VS.NET'in ilk sürümlerinde, HTTP-GET adlı üçüncü bir yöntem mevcuttu. Bu belgenin yazıldığı tarih itibarıyla (Mart 2004), bu yöntem artık kullanılamıyor gibi görünüyor. Bu, VS.NET tarafından oluşturulan web hizmetinin GET isteklerini kabul etmediği anlamına gelir. Bu, GET isteklerini kabul eden web hizmetleri yazamayacağımız anlamına gelmez; özellikle VS.NET dışındaki diğer araçlarla veya basitçe elle yazarak bu tür hizmetler oluşturulabilir.
10.5. Bir HTTP-POST istemcisi
Web hizmeti tarafından önerilen yöntemi takip ediyoruz:

Yazılanları açıklayalım. Öncelikle, web istemcisi aşağıdaki HTTP başlıklarını göndermelidir:
Web istemcisi, POST protokolü sürüm 1.1'e göre URL /operations/operations.asmx/ajouter adresine bir istek gönderir | |
İsteğin hedef makinesi belirtilir. Burada localhost. Bu başlık, HTTP protokolünün 1.1 sürümüyle zorunlu hale getirilmiştir | |
Burada, HTTP başlıklarından sonra urlencoded biçiminde ek parametreler gönderileceği belirtilir. Bu biçim, bazı karakterleri onaltılık kodlarıyla değiştirir. | |
Bu, HTTP başlıklarından sonra gönderilecek parametre dizesinin karakter sayısıdır. |
HTTP başlıklarının ardından bir boş satır gelir; ardından POST’in [Content-Length] karakterlik parametre dizisi, a=XX&b=YY biçiminde yer alır; burada XX ve YY, a ve b parametrelerinin değerlerinin "url kodlanmış" dizeleridir. TCP-IP programlama bölümünde daha önce kullandığımız genel TCP istemcimizle yukarıdakini yeniden oluşturmak için yeterli bilgiye sahibiz:
- IIS'i başlatıyoruz
- hizmet, [http://localhost/operations/operations.asmx] URL'sinde erişilebilir
- genel amaçlı TCP istemcisini DOS penceresinde kullanıyoruz
dos>clttcpgenerique localhost 80
Commandes :
POST /operations/operations.asmx/ajouter HTTP/1.1
HOST: localhost
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-length: 7
<-- HTTP/1.1 100 Continue
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 13:55:17 GMT
<-- X-Powered-By: ASP.NET
<--
a=2&b=3
<-- HTTP/1.1 200 OK
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 13:55:26 GMT
<-- X-Powered-By: ASP.NET
<-- Connection: close
<-- X-AspNet-Version: 1.1.4322
<-- Cache-Control: private, max-age=0
<-- Content-Type: text/xml; charset=utf-8
<-- Content-Length: 90
<--
<-- <?xml version="1.0" encoding="utf-8"?>
<-- <double xmlns="st.istia.univ-angers.fr">5</double>
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Öncelikle, sunucudan yanıtı gönderdikten sonra bağlantıyı kapatmasını istemek için [Connection: close] başlığını eklediğimizi belirtelim. Bu, burada gereklidir. Aksi belirtilmezse, sunucu varsayılan olarak bağlantıyı açık tutacaktır. Oysa sunucunun yanıtı, son satırı satır sonu işaretiyle bitmeyen bir dizi metin satırından oluşur. Genel amaçlı TCP istemcimiz, ReadLine yöntemi ile satır sonu işaretiyle biten metin satırlarını okur. Sunucu son satırı gönderdikten sonra bağlantıyı kapatmazsa, istemci gelmeyen bir satır sonu işaretini beklediği için kilitlenir. Sunucu bağlantıyı kapatırsa, istemcinin ReadLine yöntemi sona erer ve istemci kilitlenmez.
HTTP başlıklarının sonunu belirten boş satırı alır almaz, IIS sunucusu ilk yanıtı gönderir:
<-- HTTP/1.1 100 Continue
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 13:55:17 GMT
<-- X-Powered-By: ASP.NET
<--
Yalnızca HTTP başlıklarından oluşan bu yanıt, müşteriye göndermek istediğini belirttiği 7 karakteri gönderebileceğini bildirir. Yaptığımız şey:
Burada, TCP istemcimizin 7 karakterden fazlasını gönderdiğini görmek gerekir, çünkü bunları satır sonu işaretiyle (WriteLine) birlikte göndermektedir. Bu durum sunucu için sorun teşkil etmez; çünkü sunucu alınan karakterlerden yalnızca ilk 7'sini alır ve ardından bağlantı kapatılır (Connection: close). Bağlantı açık kalsaydı, bu fazla karakterler sorun yaratabilirdi; çünkü o zaman bunlar müşterinin bir sonraki komutundan gelen karakterler olarak algılanırdı. Parametreler alındıktan sonra, sunucu yanıtını gönderir:
<-- HTTP/1.1 200 OK
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 13:55:26 GMT
<-- X-Powered-By: ASP.NET
<-- Connection: close
<-- X-AspNet-Version: 1.1.4322
<-- Cache-Control: private, max-age=0
<-- Content-Type: text/xml; charset=utf-8
<-- Content-Length: 90
<--
<-- <?xml version="1.0" encoding="utf-8"?>
<-- <double xmlns="st.istia.univ-angers.fr">5</double>
Artık web hizmetimiz için programlanmış bir istemci yazmak için gerekli unsurlara sahibiz. Bu, httpPost2 adlı ve şu şekilde kullanılan bir konsol istemcisi olacaktır:
dos>httpPost2 http://localhost/operations/operations.asmx
Tapez vos commandes au format : [ajouter|soustraire|multiplier|diviser] a b
ajouter 6 7
--> POST /operations/operations.asmx/ajouter HTTP/1.1
--> Host: localhost:80
--> Content-Type: application/x-www-form-urlencoded
--> Content-Length: 7
--> Connection: Keep-Alive
-->
<-- HTTP/1.1 100 Continue
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 14:56:38 GMT
<-- X-Powered-By: ASP.NET
<--
--> a=6&b=7
<-- HTTP/1.1 200 OK
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 14:56:38 GMT
<-- X-Powered-By: ASP.NET
<-- X-AspNet-Version: 1.1.4322
<-- Cache-Control: private, max-age=0
<-- Content-Type: text/xml; charset=utf-8
<-- Content-Length: 91
<--
<-- <?xml version="1.0" encoding="utf-8"?>
<-- <double xmlns="st.istia.univ-angers.fr">13</double>
[résultat=13]
soustraire 8 9
--> POST /operations/operations.asmx/soustraire HTTP/1.1
--> Host: localhost:80
--> Content-Type: application/x-www-form-urlencoded
--> Content-Length: 7
--> Connection: Keep-Alive
-->
<-- HTTP/1.1 100 Continue
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 14:56:47 GMT
<-- X-Powered-By: ASP.NET
<--
--> a=8&b=9
<-- HTTP/1.1 200 OK
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 14:56:47 GMT
<-- X-Powered-By: ASP.NET
<-- X-AspNet-Version: 1.1.4322
<-- Cache-Control: private, max-age=0
<-- Content-Type: text/xml; charset=utf-8
<-- Content-Length: 91
<--
<-- <?xml version="1.0" encoding="utf-8"?>
<-- <double xmlns="st.istia.univ-angers.fr">-1</double>
[résultat=-1]
fin
dos>
Web hizmetinden gelen URL değeri iletilerek istemci çağrılır:
Ardından, istemci klavyeden girilen komutları okur ve yürütür. Bu komutlar şu formattadır:
burada "fonksiyon", çağrılan web hizmetinin fonksiyonudur (toplama, çıkarma, çarpma, bölme) ve "a" ile "b", bu fonksiyonun üzerinde işlem yapacağı değerlerdir. Örneğin:
Bu noktadan itibaren istemci, web sunucusuna gerekli olan HTTP isteğini gönderir ve bir yanıt alır. Sürecin daha iyi anlaşılması için istemci-sunucu arasındaki iletişim ekran üzerinde gösterilir:
ajouter 6 7
--> POST /operations/operations.asmx/ajouter HTTP/1.1
--> Host: localhost:80
--> Content-Type: application/x-www-form-urlencoded
--> Content-Length: 7
--> Connection: Keep-Alive
-->
<-- HTTP/1.1 100 Continue
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 14:56:38 GMT
<-- X-Powered-By: ASP.NET
<--
--> a=6&b=7
<-- HTTP/1.1 200 OK
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 14:56:38 GMT
<-- X-Powered-By: ASP.NET
<-- X-AspNet-Version: 1.1.4322
<-- Cache-Control: private, max-age=0
<-- Content-Type: text/xml; charset=utf-8
<-- Content-Length: 91
<--
<-- <?xml version="1.0" encoding="utf-8"?>
<-- <double xmlns="st.istia.univ-angers.fr">13</double>
[résultat=13]
Yukarıda, genel TCP istemcisiyle daha önce karşılaştığımız iletişim akışını tek bir farkla görüyoruz: HTTP Connection: Keep-Alive başlığı, sunucudan bağlantıyı kapatmamasını ister. Böylece bağlantı, istemcinin bir sonraki işlemi için açık kalır ve istemcinin sunucuya yeniden bağlanmasına gerek kalmaz. Ancak bu durum, sunucunun yanıtını okumak için ReadLine() dışında başka bir yöntem kullanmasını gerektirir; zira bu yanıtın, son satırı satır sonu işaretiyle bitmeyen bir dizi satırdan oluştuğu bilinmektedir. Sunucudan gelen yanıtın tamamı alındıktan sonra, istemci bunu analiz ederek istenen işlemin sonucunu bulur ve ekrana yazar:
İstemcimizin koduna bir göz atalım:
' ad alanları
Imports System
Imports System.Net.Sockets
Imports System.IO
Imports System.Text.RegularExpressions
Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Web
' web hizmeti istemcisi operations
Public Module clientPOST
Public Sub Main(ByVal args() As String)
' sözdizimi
Const syntaxe As String = "pg URI"
Dim fonctions As String() = {"ajouter", "soustraire", "multiplier", "diviser"}
' argüman sayısı
If args.Length <> 1 Then
erreur(syntaxe, 1)
End If
' istenen URI not edilir
Dim URIstring As String = args(0)
' sunucuya bağlanılıyor
Dim uri As Uri = Nothing ' l'URI du service web
Dim client As TcpClient = Nothing ' la liaison tcp du client avec le serveur
Dim [IN] As StreamReader = Nothing ' le flux de lecture du client
Dim OUT As StreamWriter = Nothing ' le flux d'écriture du client
Try
' sunucuya bağlantı
uri = New Uri(URIstring)
client = New TcpClient(uri.Host, uri.Port)
' TCP istemcisinin giriş-çıkış akışları oluşturuluyor
[IN] = New StreamReader(client.GetStream())
OUT = New StreamWriter(client.GetStream())
OUT.AutoFlush = True
Catch ex As Exception
' URI hatalı veya başka bir sorun
erreur("L'erreur suivante s'est produite : " + ex.Message, 2)
End Try
' web hizmetinin işlev sözlüğü oluşturuluyor
Dim dicoFonctions As New Hashtable
Dim i As Integer
For i = 0 To fonctions.Length - 1
dicoFonctions.Add(fonctions(i), True)
Next i
' kullanıcı istekleri klavyeyle girilir
' "fonksiyon a b" biçiminde
' "end" komutuyla sonlanıyor
Dim commande As String = Nothing ' commande tapée au clavier
Dim champs As String() = Nothing ' champs d'une ligne de commande
Dim fonction As String = Nothing ' nom d'une fonction du service web
Dim a, b As String ' les arguments des fonctions du service web
' kullanıcıya komut istemi gösterilir
Console.Out.WriteLine("Tapez vos commandes au format : [ajouter|soustraire|multiplier|diviser] a b")
' hata yönetimi
Dim erreurCommande As Boolean
Try
' klavyeden girilen komutların işlenmesi döngüsü
While True
' başlangıçta hata yok
erreurCommande = False
' komut okuma
commande = Console.In.ReadLine().Trim().ToLower()
' bitti mi?
If commande Is Nothing Or commande = "fin" Then
Exit While
End If
' komutun alanlara ayrılması
champs = Regex.Split(commande, "\s+")
Try
' üç alan gerekiyor
If champs.Length <> 3 Then
Throw New Exception
End If
' 0 numaralı alan tanınan bir işlev olmalı
fonction = champs(0)
If Not dicoFonctions.ContainsKey(fonction) Then
Throw New Exception
End If
' 1. alan geçerli bir sayı olmalı
a = champs(1)
Double.Parse(a)
' 2. alan geçerli bir sayı olmalıdır
b = champs(2)
Double.Parse(b)
Catch
' komut geçersiz
Console.Out.WriteLine("syntaxe : [ajouter|soustraire|multiplier|diviser] a b")
erreurCommande = True
End Try
' web hizmetine istekte bulunuluyor
If Not erreurCommande Then executeFonction([IN], OUT, uri, fonction, a, b)
End While
Catch e As Exception
Console.Out.WriteLine(("L'erreur suivante s'est produite : " + e.Message))
End Try
' istemci-sunucu bağlantısı sona erdi
Try
[IN].Close()
OUT.Close()
client.Close()
Catch
End Try
End Sub
...........
' hataların görüntülenmesi
Public Sub erreur(ByVal msg As String, ByVal exitCode As Integer)
' hata görüntüleniyor
System.Console.Error.WriteLine(msg)
' hata ile durduruldu
Environment.Exit(exitCode)
End Sub
End Module
Burada daha önce birkaç kez karşılaştığımız ve özel bir açıklamaya gerek duymayan unsurlar var. Şimdi, yeniliklerin yer aldığı executeFonction yönteminin koduna bakalım:
' executeFonction
Public Sub executeFonction(ByVal [IN] As StreamReader, ByVal OUT As StreamWriter, ByVal uri As Uri, ByVal fonction As String, ByVal a As String, ByVal b As String)
' URI URI'sindeki web hizmeti üzerinde (a,b) işlevini yürütür
' istemci-sunucu iletişimi, IN ve OUT akışları üzerinden gerçekleşir
' fonksiyonun sonucu şu satırda yer almaktadır
' <double xmlns="st.istia.univ-angers.fr">double</double>
' sunucu tarafından gönderildi
' istek dizesinin oluşturulması
Dim requête As String = "a=" + HttpUtility.UrlEncode(a) + "&b=" + HttpUtility.UrlEncode(b)
Dim nbChars As Integer = requête.Length
' gönderilecek HTTP başlık tablosunun oluşturulması
Dim entetes(5) As String
entetes(0) = "POST " + uri.AbsolutePath + "/" + fonction + " HTTP/1.1"
entetes(1) = "Host: " & uri.Host & ":" & uri.Port
entetes(2) = "Content-Type: application/x-www-form-urlencoded"
entetes(3) = "Content-Length: " & nbChars
entetes(4) = "Connection: Keep-Alive"
entetes(5) = ""
' HTTP başlıkları sunucuya gönderiliyor
Dim i As Integer
For i = 0 To entetes.Length - 1
' sunucuya gönderme
OUT.WriteLine(entetes(i))
' ekran çıktısı
Console.Out.WriteLine(("--> " + entetes(i)))
Next i
' Web sunucusundan gelen ilk yanıt okunur: HTTP/1.1 100
Dim ligne As String = Nothing
' okuma akışından bir satır
ligne = [IN].ReadLine()
While ligne <> ""
'yankı
Console.Out.WriteLine(("<-- " + ligne))
' sonraki satır
ligne = [IN].ReadLine()
End While
'son satır yankısı
Console.Out.WriteLine(("<-- " + ligne))
' istek parametrelerini gönder
OUT.Write(requête)
' yankı
Console.Out.WriteLine(("--> " + requête))
' yanıtın boyutunu bulmaya yarayan düzenli ifadenin oluşturulması XML
' web sunucusunun yanıt akışında
Dim modèleLength As String = "^Content-Length: (.+?)\s*$"
Dim RegexLength As New Regex(modèleLength) '
Dim MatchLength As Match = Nothing
Dim longueur As Integer = 0
' isteğin gönderilmesinden sonra web sunucusundan gelen ikinci yanıtın okunması
' Content-Length satırının değeri kaydedilir
ligne = [IN].ReadLine()
While ligne <> ""
' ekran çıktısı
Console.Out.WriteLine(("<-- " + ligne))
' Content-Length?
MatchLength = RegexLength.Match(ligne)
If MatchLength.Success Then
longueur = Integer.Parse(MatchLength.Groups(1).Value)
End If
' sonraki satır
ligne = [IN].ReadLine()
End While
' son satırı ekrana yazdır
Console.Out.WriteLine("<--")
' sonucu bulmaya yarayan düzenli ifadenin oluşturulması
' web sunucusunun yanıt akışında
Dim modèle As String = "<double xmlns=""st.istia.univ-angers.fr"">(.+?)</double>"
Dim ModèleRésultat As New Regex(modèle)
Dim MatchRésultat As Match = Nothing
' web sunucusunun yanıtının geri kalan kısmı okunur
Dim chrRéponse(longueur) As Char
[IN].Read(chrRéponse, 0, longueur)
Dim strRéponse As String = New [String](chrRéponse)
' yanıt, metin satırlarına ayrıştırılır
Dim lignes As String() = Regex.Split(strRéponse, ControlChars.Lf)
' sonucu bulmak için metin satırları taranır
Dim strRésultat As String = "?" ' résultat de la fonction
For i = 0 To lignes.Length - 1
' izleme
Console.Out.WriteLine(("<-- " + lignes(i)))
' geçerli satırın şablonla karşılaştırılması
MatchRésultat = ModèleRésultat.Match(lignes(i))
' bulundu mu?
If MatchRésultat.Success Then
' sonuç kaydediliyor
strRésultat = MatchRésultat.Groups(1).Value
End If
Next i
' sonuç görüntüleniyor
Console.Out.WriteLine(("[résultat=" + strRésultat + "]" + ControlChars.Lf))
End Sub
Öncelikle, HTTP-POST istemcisi isteğini POST biçiminde gönderir:
' sorgu dizesinin oluşturulması
Dim requête As String = "a=" + HttpUtility.UrlEncode(a) + "&b=" + HttpUtility.UrlEncode(b)
Dim nbChars As Integer = requête.Length
' gönderilecek başlık tablosu oluşturuluyor HTTP
Dim entetes(5) As String
entetes(0) = "POST " + uri.AbsolutePath + "/" + fonction + " HTTP/1.1"
entetes(1) = "Host: " & uri.Host & ":" & uri.Port
entetes(2) = "Content-Type: application/x-www-form-urlencoded"
entetes(3) = "Content-Length: " & nbChars
entetes(4) = "Connection: Keep-Alive"
entetes(5) = ""
' HTTP başlıklarını sunucuya gönderme
Dim i As Integer
For i = 0 To entetes.Length - 1
' sunucuya gönderme
OUT.WriteLine(entetes(i))
' ekran çıktısı
Console.Out.WriteLine(("--> " + entetes(i)))
Next i
Başlıkta
bölümünde, istemci tarafından HTTP başlıklarının arkasında gönderilecek parametrelerin boyutu belirtilmelidir:
Bunun için aşağıdaki kod kullanılır:
' istek dizesinin oluşturulması
Dim requête As String = "a=" + HttpUtility.UrlEncode(a) + "&b=" + HttpUtility.UrlEncode(b)
Dim nbChars As Integer = requête.Length
HttpUtility.UrlEncode yöntemi(string dizesi), chaîne'teki bazı karakterleri %n1n2'e dönüştürür; burada n1n2, dönüştürülen karakterin ASCII kodudur. Bu dönüştürmenin hedef aldığı karakterler, bir POST sorgusunda özel bir anlam taşıyan tüm karakterlerdir (boşluk, = işareti, & işareti, ...). Burada HttpUtility.UrlEncode yöntemi normalde gereksizdir, çünkü a ve b, bu özel karakterlerin hiçbirini içermeyen sayılardır. Bu yöntem burada örnek olarak kullanılmaktadır. Bu yöntem, System.Web ad alanına ihtiyaç duyar. İstemci, HTTP başlıklarını gönderdikten sonra:
--> POST /operations/operations.asmx/ajouter HTTP/1.1
--> Host: localhost:80
--> Content-Type: application/x-www-form-urlencoded
--> Content-Length: 7
--> Connection: Keep-Alive
-->
sunucu, HTTP 100 Continue başlığıyla yanıt verir:
<-- HTTP/1.1 100 Continue
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 14:56:47 GMT
<-- X-Powered-By: ASP.NET
<--
Kod, yalnızca bu ilk yanıtı okuyup ekrana görüntüler:
' Web sunucusundan gelen ilk yanıt okunur HTTP/1.1 100
Dim ligne As String = Nothing
' okuma akışından bir satır
ligne = [IN].ReadLine()
While ligne <> ""
'yankı
Console.Out.WriteLine(("<-- " + ligne))
' sonraki satır
ligne = [IN].ReadLine()
End While
'son satır yankısı
Console.Out.WriteLine(("<-- " + ligne))
Bu ilk yanıtı okuduktan sonra, müşteri parametrelerini göndermelidir:
Bunu aşağıdaki kodla yapar:
' istek parametrelerinin gönderilmesi
OUT.Write(requête)
' yankı
Console.Out.WriteLine(("--> " + requête))
Ardından sunucu yanıtını gönderir. Bu yanıt iki bölümden oluşur:
- boş bir satırla biten HTTP başlıkları
- XML biçimindeki yanıt
<-- HTTP/1.1 200 OK
<-- Server: Microsoft-IIS/5.0
<-- Date: Wed, 03 Mar 2004 14:56:38 GMT
<-- X-Powered-By: ASP.NET
<-- X-AspNet-Version: 1.1.4322
<-- Cache-Control: private, max-age=0
<-- Content-Type: text/xml; charset=utf-8
<-- Content-Length: 91
<--
<-- <?xml version="1.0" encoding="utf-8"?>
<-- <double xmlns="st.istia.univ-angers.fr">13</double>
İlk olarak, istemci HTTP başlıklarını okur, burada Content-Length satırını bulur ve XML yanıtının boyutunu (burada 90) alır. Bu değer, bir düzenli ifade kullanılarak elde edilir. Bunu başka bir şekilde ve muhtemelen daha verimli bir şekilde de yapabilirdik.
' yanıtın boyutunu bulmaya yarayan düzenli ifadenin oluşturulması XML
' web sunucusunun yanıt akışında
Dim modèleLength As String = "^Content-Length: (.+?)\s*$"
Dim RegexLength As New Regex(modèleLength) '
Dim MatchLength As Match = Nothing
Dim longueur As Integer = 0
' isteğin gönderilmesinden sonra web sunucusundan gelen ikinci yanıtın okunması
' Content-Length satırının değeri kaydedilir
ligne = [IN].ReadLine()
While ligne <> ""
' ekran çıktısı
Console.Out.WriteLine(("<-- " + ligne))
' Content-Length?
MatchLength = RegexLength.Match(ligne)
If MatchLength.Success Then
longueur = Integer.Parse(MatchLength.Groups(1).Value)
End If
' sonraki satır
ligne = [IN].ReadLine()
End While
' son satırı ekrana yazdır
Console.Out.WriteLine("<--")
XML yanıtının uzunluğu N elde edildiğinde, sunucunun yanıtındaki IN akışından N karakter okumak yeterlidir. Bu N karakterlik dize, ekran izleme amacıyla metin satırlarına ayrıştırılır. Bu satırlar arasında sonuç satırı aranır:
yine bir düzenli ifade kullanılarak aranır. Sonuç bulunduktan sonra ekrana görüntülenir.
İstemci kodunun sonu şu şekildedir:
' sonucu bulmaya yarayan düzenli ifadenin oluşturulması
' web sunucusunun yanıt akışında
Dim modèle As String = "<double xmlns=""st.istia.univ-angers.fr"">(.+?)</double>"
Dim ModèleRésultat As New Regex(modèle)
Dim MatchRésultat As Match = Nothing
' web sunucusunun yanıtının geri kalan kısmı okunur
Dim chrRéponse(longueur) As Char
[IN].Read(chrRéponse, 0, longueur)
Dim strRéponse As String = New [String](chrRéponse)
' yanıt, metin satırlarına ayrıştırılır
Dim lignes As String() = Regex.Split(strRéponse, ControlChars.Lf)
' sonucu bulmak için metin satırları taranır
Dim strRésultat As String = "?" ' résultat de la fonction
For i = 0 To lignes.Length - 1
' izleme
Console.Out.WriteLine(("<-- " + lignes(i)))
' geçerli satır şablonla karşılaştırılır
MatchRésultat = ModèleRésultat.Match(lignes(i))
' bulundu mu?
If MatchRésultat.Success Then
' sonuç kaydediliyor
strRésultat = MatchRésultat.Groups(1).Value
End If
Next i
' sonuç görüntüleniyor
Console.Out.WriteLine(("[résultat=" + strRésultat + "]" + ControlChars.Lf))
End Sub
10.6. Bir SOAP istemcisi
Burada, SOAP (Basit Nesne Erişim Protokolü) türünde bir istemci-sunucu diyaloğu kullanacak ikinci bir istemciyi inceliyoruz. ajouter işlevi için bir diyalog örneği sunulmaktadır:


İstemcinin isteği, POST isteğidir. Dolayısıyla, önceki istemcinin bazı mekanizmalarını burada da göreceğiz. Temel fark şudur: HTTP-POST istemcisi, a ve b parametrelerini
şeklinde gönderirken, SOAP istemcisi bunları daha karmaşık bir XML biçiminde gönderiyor:
POST /operations/operations.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "st.istia.univ-angers.fr/ajouter"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ajouter xmlns="st.istia.univ-angers.fr">
<a>double</a>
<b>double</b>
</ajouter>
</soap:Body>
</soap:Envelope>
Karşılığında, daha önce görülen yanıtlardan daha karmaşık olan XML yanıtını alır:
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ajouterResponse xmlns="st.istia.univ-angers.fr">
<ajouterResult>double</ajouterResult>
</ajouterResponse>
</soap:Body>
</soap:Envelope>
İstek ve yanıt daha karmaşık olsa da, bu mekanizma HTTP müşterisi için olduğu gibi aynı mekanizmadır. SOAP istemcisinin kod yazımı, HTTP-POST istemcisininkine benzer şekilde yazılabilir. İşte bir yürütme örneği:
dos>clientsoap1 http://localhost/operations/operations.asmx
Tapez vos commandes au format : [ajouter|soustraire|multiplier|diviser] a b
ajouter 3 4
--> POST /operations/operations.asmx HTTP/1.1
--> Host: localhost:80
--> Content-Type: text/xml; charset=utf-8
--> Content-Length: 321
--> Connection: Keep-Alive
--> SOAPAction: "st.istia.univ-angers.fr/ajouter"
-->
<-- HTTP/1.1 100 Continue
<-- Server: Microsoft-IIS/5.0
<-- Date: Thu, 04 Mar 2004 07:28:29 GMT
<-- X-Powered-By: ASP.NET
<--
--> <?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ajouter xmlns="st.istia.univ-angers.fr">
<a>3</a>
<b>4</b>
</ajouter>
</soap:Body>
</soap:Envelope>
<-- HTTP/1.1 200 OK
<-- Server: Microsoft-IIS/5.0
<-- Date: Thu, 04 Mar 2004 07:28:33 GMT
<-- X-Powered-By: ASP.NET
<-- X-AspNet-Version: 1.1.4322
<-- Cache-Control: private, max-age=0
<-- Content-Type: text/xml; charset=utf-8
<-- Content-Length: 345
<--
<-- <?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-inst
ance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><ajouterResponse xmlns="st.istia.univ-angers.fr"><ajouterResult>7</ajouterResult></ajouterResponse
></soap:Body></soap:Envelope>
[résultat=7]
Yalnızca executeFonction yöntemi değişir. SOAP istemcisi, isteğinin HTTP başlıklarını gönderir. Bunlar, HTTP-POST'inkilerden sadece biraz daha karmaşıktır:
ajouter 3 4
--> POST /operations/operations.asmx HTTP/1.1
--> Host: localhost:80
--> Content-Type: text/xml; charset=utf-8
--> Content-Length: 321
--> Connection: Keep-Alive
--> SOAPAction: "st.istia.univ-angers.fr/ajouter"
-->
Bunları oluşturan kod:
' executeFonction
Public Sub executeFonction(ByVal [IN] As StreamReader, ByVal OUT As StreamWriter, ByVal uri As Uri, ByVal fonction As String, ByVal a As String, ByVal b As String)
' URI URI'sindeki web hizmeti üzerinde (a,b) işlevini yürütür
' istemci-sunucu iletişimi, IN ve OUT akışları üzerinden gerçekleşir
' fonksiyonun sonucu şu satırda yer almaktadır
' <double xmlns="st.istia.univ-angers.fr">double</double>
' sunucu tarafından gönderildi
' istek dizesinin oluşturulması SOAP
Dim requêteSOAP As String = "<?xml version=" + """1.0"" encoding=""utf-8""?>" + ControlChars.Lf
requêteSOAP += "<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">" + ControlChars.Lf
requêteSOAP += "<soap:Body>" + ControlChars.Lf
requêteSOAP += "<" + fonction + " xmlns=""st.istia.univ-angers.fr"">" + ControlChars.Lf
requêteSOAP += "<a>" + a + "</a>" + ControlChars.Lf
requêteSOAP += "<b>" + b + "</b>" + ControlChars.Lf
requêteSOAP += "</" + fonction + ">" + ControlChars.Lf
requêteSOAP += "</soap:Body>" + ControlChars.Lf
requêteSOAP += "</soap:Envelope>"
Dim nbCharsSOAP As Integer = requêteSOAP.Length
' gönderilecek başlık tablosunun oluşturulması HTTP
Dim entetes(6) As String
entetes(0) = "POST " + uri.AbsolutePath + " HTTP/1.1"
entetes(1) = "Host: " & uri.Host & ":" & uri.Port
entetes(2) = "Content-Type: text/xml; charset=utf-8"
entetes(3) = "Content-Length: " & nbCharsSOAP
entetes(4) = "Connection: Keep-Alive"
entetes(5) = "SOAPAction: ""st.istia.univ-angers.fr/" + fonction + """"
entetes(6) = ""
' HTTP başlıkları sunucuya gönderiliyor
Dim i As Integer
For i = 0 To entetes.Length - 1
' sunucuya gönderme
OUT.WriteLine(entetes(i))
' ekran çıktısı
Console.Out.WriteLine(("--> " + entetes(i)))
Next i
Bu isteği aldığında, sunucu ilk yanıtını gönderir ve istemci bunu görüntüler:
<-- HTTP/1.1 100 Continue
<-- Server: Microsoft-IIS/5.0
<-- Date: Thu, 04 Mar 2004 07:28:29 GMT
<-- X-Powered-By: ASP.NET
<--
Bu ilk cevabın okuma kodu şöyledir:
' Web sunucusundan gelen ilk yanıt okunur: HTTP/1.1 100
Dim ligne As String = Nothing
' okuma akışından bir satır
ligne = [IN].ReadLine()
While ligne <> ""
'ekran çıktısı
Console.Out.WriteLine(("<-- " + ligne))
' sonraki satır
ligne = [IN].ReadLine()
End While 'while
'son satırın yankısı
Console.Out.WriteLine(("<-- " + ligne))
Müşteri şimdi parametrelerini XML biçiminde, SOAP adı verilen bir zarf içinde gönderecek:
--> <?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ajouter xmlns="st.istia.univ-angers.fr">
<a>3</a>
<b>4</b>
</ajouter>
</soap:Body>
</soap:Envelope>
Kod:
Sunucu daha sonra nihai yanıtını gönderecektir:
<-- HTTP/1.1 200 OK
<-- Server: Microsoft-IIS/5.0
<-- Date: Thu, 04 Mar 2004 07:28:33 GMT
<-- X-Powered-By: ASP.NET
<-- X-AspNet-Version: 1.1.4322
<-- Cache-Control: private, max-age=0
<-- Content-Type: text/xml; charset=utf-8
<-- Content-Length: 345
<--
<-- <?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-inst
ance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><ajouterResponse xmlns="st.istia.univ-angers.fr"><ajouterResult>7</ajouterResult></ajouterResponse
></soap:Body></soap:Envelope>
Müşteri, alınan HTTP başlıklarını ekranda görüntülerken, Content-Length satırını arar:
' yanıtın boyutunu bulmaya yarayan düzenli ifadenin oluşturulması XML
' web sunucusunun yanıt akışında
Dim modèleLength As String = "^Content-Length: (.+?)\s*$"
Dim RegexLength As New Regex(modèleLength) '
Dim MatchLength As Match = Nothing
Dim longueur As Integer = 0
' isteğin gönderilmesinden sonra web sunucusundan gelen ikinci yanıtın okunması
' Content-Length satırının değeri kaydedilir
ligne = [IN].ReadLine()
While ligne <> ""
' ekran çıktısı
Console.Out.WriteLine(("<-- " + ligne))
' Content-Length?
MatchLength = RegexLength.Match(ligne)
If MatchLength.Success Then
longueur = Integer.Parse(MatchLength.Groups(1).Value)
End If
' sonraki satır
ligne = [IN].ReadLine()
End While 'while
' son satırı ekrana yazdır
Console.Out.WriteLine("<--")
XML yanıtının boyutu N öğrenildikten sonra, istemci sunucunun yanıt akışından N karakter okur, alınan dizeyi metin satırlarına ayırarak ekrana görüntüler ve sonuçtaki XML etiketini arar: <ajouterResult>7</ajouterResult> ve bunu görüntüler:
' sonucu bulmaya yarayan düzenli ifadenin oluşturulması
' web sunucusunun yanıt akışında
Dim modèle As String = "<" + fonction + "Result>(.+?)</" + fonction + "Result>"
Dim ModèleRésultat As New Regex(modèle)
Dim MatchRésultat As Match = Nothing
' web sunucusunun yanıtının geri kalan kısmı okunur
Dim chrRéponse(longueur) As Char
[IN].Read(chrRéponse, 0, longueur)
Dim strRéponse As String = New [String](chrRéponse)
' yanıt, metin satırlarına ayrıştırılır
Dim lignes As String() = Regex.Split(strRéponse, ControlChars.Lf)
' sonucu bulmak için metin satırları taranır
Dim strRésultat As String = "?" ' résultat de la fonction
For i = 0 To lignes.Length - 1
' izleme
Console.Out.WriteLine(("<-- " + lignes(i)))
' geçerli satırın şablonla karşılaştırılması
MatchRésultat = ModèleRésultat.Match(lignes(i))
' bulundu mu?
If MatchRésultat.Success Then
' sonuç kaydediliyor
strRésultat = MatchRésultat.Groups(1).Value
End If
'bir sonraki satır
Next i
' sonuç görüntüleniyor
Console.Out.WriteLine(("[résultat=" + strRésultat + "]" + ControlChars.Lf))
End Sub
10.7. İstemci-sunucu iletişiminin kapsüllenmesi
operations web hizmetimizin çeşitli uygulamalar tarafından kullanıldığını varsayalım. Bu uygulamalara, istemci uygulaması ile web hizmeti arasında bir arayüz görevi gören ve çoğu geliştirici için karmaşık olan ağ iletişiminin büyük bir kısmını gizleyen bir sınıf sunmak faydalı olacaktır. Böylece aşağıdaki şemaya sahip oluruz:
![]() |
İstemci uygulaması, web hizmetine isteklerini iletmek için istemci-sunucu arayüzüne başvurur. Bu arayüz, sunucuyla gerekli tüm ağ iletişimini gerçekleştirir ve elde edilen sonucu istemci uygulamasına geri döndürür. Böylece istemci uygulaması, sunucuyla olan iletişimi artık kendisinin halletmesi gerekmez ve bu da uygulamanın yazılmasını büyük ölçüde kolaylaştırır.
10.7.1. Kapsülleme sınıfı
Önceki paragraflarda gördüklerimizden sonra, artık istemci ile sunucu arasındaki ağ iletişimini iyi biliyoruz. Hatta üç yöntem gördük. SOAP yöntemini kapsüllemeyi seçiyoruz. Sınıf şu şekildedir:
' ad alanları
Imports System
Imports System.Net.Sockets
Imports System.IO
Imports System.Text.RegularExpressions
Imports System.Collections
Imports System.Web
Imports Microsoft.VisualBasic
' Web hizmetleri operasyonlarından clientSOAP
Public Class clientSOAP
' örnek değişkenleri
Private uri As uri = Nothing ' l'URI du service web
Private client As TcpClient = Nothing ' la liaison tcp du client avec le serveur
Private [IN] As StreamReader = Nothing ' le flux de lecture du client
Private OUT As StreamWriter = Nothing ' le flux d'écriture du client
' fonksiyon sözlüğü
Private dicoFonctions As New Hashtable
' fonksiyon listesi
Private fonctions As String() = {"ajouter", "soustraire", "multiplier", "diviser"}
' ayrıntılı çıktı
Private verbose As Boolean = False ' à vrai, affiche à l'écran les échanges client-serveur
' oluşturucu
Public Sub New(ByVal uriString As String, ByVal verbose As Boolean)
' ayrıntılı çıktı
Me.verbose = verbose
' sunucuya bağlanma
uri = New Uri(uriString)
client = New TcpClient(uri.Host, uri.Port)
' TCP istemcisinin giriş-çıkış akışları oluşturulur
[IN] = New StreamReader(client.GetStream())
OUT = New StreamWriter(client.GetStream())
OUT.AutoFlush = True
' web hizmetinin işlev sözlüğünün oluşturulması
Dim i As Integer
For i = 0 To fonctions.Length - 1
dicoFonctions.Add(fonctions(i), True)
Next i
End Sub
' sunucu bağlantısının kapatılması
Public Sub Close()
' istemci-sunucu bağlantısının sonlandırılması
[IN].Close()
OUT.Close()
client.Close()
End Sub
' executeFonction
Public Function executeFonction(ByVal fonction As String, ByVal a As String, ByVal b As String) As String
' URI URI'sindeki web hizmetinde (a,b) işlevini yürütür
' istemci-sunucu iletişimi, IN ve OUT akışları üzerinden gerçekleşir
' fonksiyonun sonucu şu satırda yer almaktadır
' <double xmlns="st.istia.univ-angers.fr">double</double>
' sunucu tarafından gönderildi
' fonksiyon geçerli mi?
fonction = fonction.Trim().ToLower()
If Not dicoFonctions.ContainsKey(fonction) Then
Return "[fonction [" + fonction + "] indisponible : (ajouter, soustraire,multiplier,diviser)]"
End If
' a ve b argümanları geçerli mi?
Dim doubleA As Double = 0
Try
doubleA = Double.Parse(a)
Catch
Return "[argument [" + a + "] incorrect (double)]"
End Try
Dim doubleB As Double = 0
Try
doubleB = Double.Parse(b)
Catch
Return "[argument [" + b + "] incorrect (double)]"
End Try
' sıfıra bölme var mı?
If fonction = "diviser" And doubleB = 0 Then
Return "[division par zéro]"
End If
' istek dizesinin oluşturulması SOAP
Dim requêteSOAP As String = "<?xml version=" + """1.0"" encoding=""utf-8""?>" + ControlChars.Lf
requêteSOAP += "<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">" + ControlChars.Lf
requêteSOAP += "<soap:Body>" + ControlChars.Lf
requêteSOAP += "<" + fonction + " xmlns=""st.istia.univ-angers.fr"">" + ControlChars.Lf
requêteSOAP += "<a>" + a + "</a>" + ControlChars.Lf
requêteSOAP += "<b>" + b + "</b>" + ControlChars.Lf
requêteSOAP += "</" + fonction + ">" + ControlChars.Lf
requêteSOAP += "</soap:Body>" + ControlChars.Lf
requêteSOAP += "</soap:Envelope>"
Dim nbCharsSOAP As Integer = requêteSOAP.Length
' gönderilecek başlık tablosunun oluşturulması HTTP
Dim entetes(6) As String
entetes(0) = "POST " + uri.AbsolutePath + " HTTP/1.1"
entetes(1) = "Host: " + uri.Host + ":" + uri.Port.ToString
entetes(2) = "Content-Type: text/xml; charset=utf-8"
entetes(3) = "Content-Length: " + nbCharsSOAP.ToString
entetes(4) = "Connection: Keep-Alive"
entetes(5) = "SOAPAction: ""st.istia.univ-angers.fr/" + fonction + """"
entetes(6) = ""
' HTTP başlıklarını sunucuya gönderiyoruz
Dim i As Integer
For i = 0 To entetes.Length - 1
' sunucuya gönderme
OUT.WriteLine(entetes(i))
' ekran çıktısı
If verbose Then
Console.Out.WriteLine(("--> " + entetes(i)))
End If
Next i
' Web sunucusundan gelen ilk yanıt okunur: HTTP/1.1 100
Dim ligne As String = Nothing
' okuma akışından bir satır
ligne = [IN].ReadLine()
While ligne <> ""
'yankı
If verbose Then
Console.Out.WriteLine(("<-- " + ligne))
End If
' sonraki satır
ligne = [IN].ReadLine()
End While
'son satır yankısı
If verbose Then
Console.Out.WriteLine(("<-- " + ligne))
End If
' istek parametrelerinin gönderilmesi
OUT.Write(requêteSOAP)
' yankı
If verbose Then
Console.Out.WriteLine(("--> " + requêteSOAP))
End If
' yanıtın boyutunu bulmaya yarayan düzenli ifadenin oluşturulması XML
' web sunucusunun yanıt akışında
Dim modèleLength As String = "^Content-Length: (.+?)\s*$"
Dim RegexLength As New Regex(modèleLength) '
Dim MatchLength As Match = Nothing
Dim longueur As Integer = 0
' isteğin gönderilmesinden sonra web sunucusundan gelen ikinci yanıtın okunması
' Content-Length satırının değeri kaydedilir
ligne = [IN].ReadLine()
While ligne <> ""
' ekran çıktısı
If verbose Then
Console.Out.WriteLine(("<-- " + ligne))
End If
' Content-Length ?
MatchLength = RegexLength.Match(ligne)
If MatchLength.Success Then
longueur = Integer.Parse(MatchLength.Groups(1).Value)
End If
' sonraki satır
ligne = [IN].ReadLine()
End While
' son satırı ekrana yazdır
If verbose Then
Console.Out.WriteLine("<--")
End If
' sonucu bulmaya yarayan düzenli ifadenin oluşturulması
' web sunucusunun yanıt akışında
Dim modèle As String = "<" + fonction + "Result>(.+?)</" + fonction + "Result>"
Dim ModèleRésultat As New Regex(modèle)
Dim MatchRésultat As Match = Nothing
' web sunucusunun yanıtının geri kalan kısmı okunur
Dim chrRéponse(longueur) As Char
[IN].Read(chrRéponse, 0, longueur)
Dim strRéponse As String = New [String](chrRéponse)
' yanıt, metin satırlarına ayrıştırılır
Dim lignes As String() = Regex.Split(strRéponse, ControlChars.Lf)
' sonucu bulmak için metin satırları taranır
Dim strRésultat As String = "?" ' résultat de la fonction
For i = 0 To lignes.Length - 1
' takip
If verbose Then
Console.Out.WriteLine(("<-- " + lignes(i)))
End If ' comparaison ligne courante au modèle
MatchRésultat = ModèleRésultat.Match(lignes(i))
' bulundumu?
If MatchRésultat.Success Then
' sonuç kaydedilir
strRésultat = MatchRésultat.Groups(1).Value
End If
Next i
' sonuç geri gönderilir
Return strRésultat
End Function
End Class
Daha önce gördüklerimize kıyasla yeni bir şey görmüyoruz. Sadece incelediğimiz SOAP istemci kodunu aldık ve bir sınıf haline getirmek için biraz yeniden düzenledik. Bu sınıfın bir oluşturucusu ve iki yöntemi vardır:
' oluşturucu
Public Sub New(ByVal uriString As String, ByVal verbose As Boolean)
' executeFonction
Public Function executeFonction(ByVal fonction As String, ByVal a As String, ByVal b As String) As String
' sunucu bağlantısı kapatılıyor
Public Sub Close()
ve şu özelliklere sahiptir:
' örnek değişkenleri
Private uri As Uri = Nothing ' l'URI du service web
Private client As TcpClient = Nothing ' la liaison tcp du client avec le serveur
Private [IN] As StreamReader = Nothing ' le flux de lecture du client
Private OUT As StreamWriter = Nothing ' le flux d'écriture du client
' fonksiyon sözlüğü
Private dicoFonctions As New Hashtable
' fonksiyon listesi
Private fonctions As String() = {"ajouter", "soustraire", "multiplier", "diviser"}
' ayrıntılı çıktı
Private verbose As Boolean = False ' à vrai, affiche à l'écran les échanges client-serveur
Oluşturucuya iki parametre aktarılır:
- bağlanması gereken web hizmetinin URI değeri
- bir boole değeri olan verbose; bu değer, ağ iletişimlerinin ekranda görüntülenmesini talep eder; aksi takdirde görüntülenmez.
Oluşturma işlemi sırasında, ağ okuma akışı IN, ağ yazma akışı OUT ve hizmet tarafından yönetilen işlevler sözlüğü oluşturulur. Nesne oluşturulduktan sonra, istemci-sunucu bağlantısı açılır ve IN ile OUT akışları kullanılabilir hale gelir.
Close yöntemi, sunucu ile olan bağlantıyı kapatmaya yarar.
ExecuteFonction yöntemi, incelediğimiz SOAP istemcisi için yazdığımız yöntemle, birkaç küçük ayrıntı dışında aynıdır:
- Daha önce yönteme parametre olarak geçirilen uri, IN ve OUT parametrelerinin artık geçirilmesine gerek yoktur, çünkü bunlar artık örneğin tüm yöntemleri tarafından erişilebilen örnek öznitelikleridir
- Daha önce void türünde bir değer döndüren ve fonksiyonun sonucunu ekrana yazdıran ExecuteFonction yöntemi, artık bu sonucu döndürüyor ve dolayısıyla string türünde bir değer veriyor.
Genellikle bir istemci, clientSOAP sınıfını şu şekilde kullanır:
- web hizmeti ile bağlantıyı kuracak bir clientSOAP nesnesi oluşturulur
- executeFonction yönteminin tekrar tekrar kullanılması
- Close yöntemi ile Web hizmeti bağlantısının kapatılması.
İlk istemciyi inceleyelim.
10.7.2. Konsol istemcisi
Burada, clientSOAP sınıfı henüz mevcut değilken incelediğimiz SOAP istemcisini ele alıyoruz ve artık bu sınıfı kullanacak şekilde yeniden düzenliyoruz:
' ad alanları
Imports System
Imports System.IO
Imports System.Text.RegularExpressions
Imports Microsoft.VisualBasic
Public Module testClientSoap
' web hizmeti operations'tan URI'i ister
' klavyeden girilen komutları etkileşimli olarak yürütür
Public Sub Main(ByVal args() As String)
' sözdizimi
Const syntaxe As String = "pg URI [verbose]"
' argüman sayısı
If args.Length <> 1 And args.Length <> 2 Then
erreur(syntaxe, 1)
End If
' ayrıntılı çıktı?
Dim verbose As Boolean = False
If args.Length = 2 Then
verbose = args(1).ToLower() = "verbose"
End If
' web hizmetine bağlanılır
Dim client As clientSOAP = Nothing
Try
client = New clientSOAP(args(0), verbose)
Catch ex As Exception
' bağlantı hatası
erreur("L'erreur suivante s'est produite lors de la connexion au service web : " + ex.Message, 2)
End Try
' kullanıcı istekleri klavyeyle girilir
' a b işlevi biçiminde - 'fin' komutuyla sona erer
Dim commande As String = Nothing ' commande tapée au clavier
Dim champs As String() = Nothing ' champs d'une ligne de commande
' kullanıcıya komut istemi
Console.Out.WriteLine("Tapez vos commandes au format : [ajouter|soustraire|multiplier|diviser] a b" + ControlChars.Lf)
' hata yönetimi
Dim erreurCommande As Boolean
Try
' klavyeden girilen komutların işleme döngüsü
While True
' başlangıçta hata yok
erreurCommande = False
' komut okuma
commande = Console.In.ReadLine().Trim().ToLower()
' bitti mi?
If commande Is Nothing Or commande = "fin" Then
Exit While
End If
' komutun alanlara ayrılması
champs = Regex.Split(commande, "\s+")
' üç alan gerekiyor
If champs.Length <> 3 Then
Console.Out.WriteLine("syntaxe : [ajouter|soustraire|multiplier|diviser] a b")
' hata kaydedildi
erreurCommande = True
End If
' web hizmetine istek gönderiliyor
If Not erreurCommande Then Console.Out.WriteLine(("résultat=" + client.executeFonction(champs(0).Trim().ToLower(), champs(1).Trim(), champs(2).Trim())))
' sonraki istek
End While
Catch e As Exception
Console.Out.WriteLine(("L'erreur suivante s'est produite : " + e.Message))
End Try
' istemci-sunucu bağlantısı sona erdi
Try
client.Close()
Catch
End Try
End Sub
' hataların görüntülenmesi
Public Sub erreur(ByVal msg As String, ByVal exitCode As Integer)
' hatanın görüntülenmesi
System.Console.Error.WriteLine(msg)
' hata ile durdurma
Environment.Exit(exitCode)
End Sub
End Module
İstemci artık önemli ölçüde daha basit hale geldi ve içinde hiçbir ağ iletişimi bulunmuyor. İstemci iki parametre kabul ediyor:
- URI web hizmetinin operations
- isteğe bağlı anahtar kelime verbose. Bu anahtar kelime varsa, ağ iletişimi ekranda görüntülenecektir.
Bu iki parametre, web hizmeti ile iletişimi sağlayacak bir clientSOAP nesnesi oluşturmak için kullanılır.
' web hizmetine bağlanılıyor
Dim client As clientSOAP = Nothing
Try
client = New clientSOAP(args(0), verbose)
Catch ex As Exception
' bağlantı hatası
erreur("L'erreur suivante s'est produite lors de la connexion au service web : " + ex.Message, 2)
End Try
Web hizmeti ile bağlantı kurulduktan sonra, istemci isteklerini gönderebilir. Bu istekler klavyeyle girilir, analiz edilir ve ardından clientSOAP nesnesinin executeFonction yöntemine çağrı yapılarak sunucuya gönderilir.
' web hizmetine istek gönderiliyor
If Not erreurCommande Then Console.Out.WriteLine(("résultat=" + client.executeFonction(champs(0).Trim().ToLower(), champs(1).Trim(), champs(2).Trim())))
clientSOAP sınıfı bir "derleme" içinde derlenir:
dos>vbc /r:clientSOAP.dll testClientSOAP.vb
dos>dir
04/03/2004 08:46 6 913 clientSOAP.vb
04/03/2004 09:07 7 168 clientSOAP.dll
testClientSoap istemci uygulaması daha sonra şu şekilde derlenir:
dos>vbc /r:clientSOAP.dll /r:system.dll testClientSOAP.vb
dos>dir
04/03/2004 09:08 2 711 testClientSOAP.vb
04/03/2004 09:08 4 608 testClientSOAP.exe
İşte ayrıntılı olmayan bir yürütme örneği:
dos>testclientsoap http://localhost/st/operations/operations.asmx
Tapez vos commandes au format : [ajouter|soustraire|multiplier|diviser] a b
ajouter 1 3
résultat=4
soustraire 6 7
résultat=-1
multiplier 4 5
résultat=20
diviser 1 2
résultat=0.5
x
syntaxe : [ajouter|soustraire|multiplier|diviser] a b
x 1 2
résultat=[fonction [x] indisponible : (ajouter, soustraire,multiplier,diviser)]
ajouter a b
résultat=[argument [a] incorrect (double)]
ajouter 1 b
résultat=[argument [b] incorrect (double)]
diviser 1 0
résultat=[division par zéro]
fin
"Ayrıntılı" bir çalıştırma talep ederek ağ trafiğini takip edebiliriz:
dos>testClientSOAP http://localhost/operations/operations.asmx ayrıntılı
Tapez vos commandes au format : [ajouter|soustraire|multiplier|diviser] a b
ajouter 4 8
--> POST /operations/operations.asmx HTTP/1.1
--> Host: localhost:80
--> Content-Type: text/xml; charset=utf-8
--> Content-Length: 321
--> Connection: Keep-Alive
--> SOAPAction: "st.istia.univ-angers.fr/ajouter"
-->
<-- HTTP/1.1 100 Continue
<-- Server: Microsoft-IIS/5.0
<-- Date: Thu, 04 Mar 2004 08:15:25 GMT
<-- X-Powered-By: ASP.NET
<--
--> <?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ajouter xmlns="st.istia.univ-angers.fr">
<a>4</a>
<b>8</b>
</ajouter>
</soap:Body>
</soap:Envelope>
<-- HTTP/1.1 200 OK
<-- Server: Microsoft-IIS/5.0
<-- Date: Thu, 04 Mar 2004 08:15:25 GMT
<-- X-Powered-By: ASP.NET
<-- X-AspNet-Version: 1.1.4322
<-- Cache-Control: private, max-age=0
<-- Content-Type: text/xml; charset=utf-8
<-- Content-Length: 346
<--
<-- <?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-inst
ance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><ajouterResponse xmlns="st.istia.univ-angers.fr"><ajouterResult>12</ajouterResult></ajouterResponse></soap:Body></soap:Envelope>
résultat=12
fin
Şimdi bir grafik istemcisi oluşturalım.
10.7.3. Bir Windows grafik istemcisi
Şimdi, yine clientSOAP sınıfını kullanacak bir grafik istemciyle web hizmetimizi sorgulayacağız. Grafik arayüzü şu şekilde olacaktır:
![]() |
Kontroller şunlardır:
No | tür | ad | rol |
TextBox | txtURI | web operations hizmetinin URI'i | |
Düğme | btnOuvrir | Web hizmetiyle bağlantıyı açar | |
Düğme | btnFermer | Web hizmeti ile bağlantıyı kapatır | |
ComboBox | cmbFonctions | işlemlerin listesi (toplama, çıkarma, çarpma, bölme) | |
TextBox | txtA | işlevlerin argümanları | |
TextBox | txtB | fonksiyonların b argümanı | |
TextBox | txtRésultat | (a, b) fonksiyonunun sonucu | |
Düğme | btnCalculer | (a,b) fonksiyonunun hesaplanmasını başlatır | |
TextBox | txtErreur | bağlantının durum mesajını görüntüler |
Bazı çalışma kısıtlamaları vardır:
- btnOuvrir düğmesi, yalnızca txtURI alanı boş değilse ve halihazırda açık bir bağlantı yoksa etkin hale gelir
- btnFermer düğmesi, web hizmeti ile bir bağlantı açıldığında etkin hale gelir
- btnCalculer düğmesi, yalnızca bir bağlantı açık olduğunda ve txtA ile txtB alanları boş olmadığında etkindir
- txtRésultat ve txtErreur alanlarının ReadOnly özniteliği "true" değerindeyse
İstemci, öncelikle [Ouvrir] düğmesini kullanarak web hizmeti ile bağlantı kurar:

Ardından kullanıcı bir işlev ve a ile b için değerler seçebilir:





Uygulamanın kodu aşağıdadır. Burada önemi olmayan form kodunu çıkardık.
'ad alanları
Imports System
Imports System.Windows.Forms
' form sınıfı
Public Class FormClientSOAP
Inherits System.Windows.Forms.Form
' örnek öznitelikleri
Dim client As clientSOAP ' client SOAP du service web operations
#Bölge "Windows Form Tasarımcısı tarafından oluşturulan kod"
Public Sub New()
MyBase.New()
'Bu çağrı, Windows Form Tasarımcısı tarafından gereklidir.
InitializeComponent()
' diğer başlatma işlemleri
myInit()
End Sub
'Formun Dispose yönteminin, bileşen listesini temizlemek için değiştirilmiş hali.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
....
End Sub
...
Private Sub InitializeComponent()
....
End Sub
#Bölge Sonu
Private Sub myInit()
' form başlatma
cmbFonctions.SelectedIndex = 0
btnOuvrir.Enabled = False
btnFermer.Enabled = True
btnCalculer.Enabled = False
End Sub
Private Sub txtURI_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtURI.TextChanged
' giriş alanının içeriği değişti - aç düğmesinin durumu ayarlanıyor
btnOuvrir.Enabled = txtURI.Text.Trim <> ""
End Sub
Private Sub btnOuvrir_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnOuvrir.Click
' web hizmeti ile bağlantı açma isteği
Try
' [clientSOAP] türünde bir nesne oluşturuldu
client = New clientSOAP(txtURI.Text.Trim, False)
' düğmelerin durumları
btnOuvrir.Enabled = False
btnFermer.Enabled = True
' URI artık değiştirilemez
txtURI.ReadOnly = True
' müşteri durumu
txtErreur.Text = "Liaison au service web ouverte"
Catch ex As Exception
' bir hata oluştu - hata mesajı görüntüleniyor
txtErreur.Text = ex.Message
End Try
End Sub
Private Sub btnFermer_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnFermer.Click
' web hizmeti bağlantısı kapatılıyor
client.Close()
' düğme durumları
btnOuvrir.Enabled = True
btnFermer.Enabled = False
' URI
txtURI.ReadOnly = False
' müşteri durumu
txtErreur.Text = "Liaison au service web fermée"
End Sub
Private Sub btnCalculer_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
' f(a,b) fonksiyonunun hesaplanması
' önceki sonucu silme
txtRésultat.Text = ""
Try
txtRésultat.Text = client.executeFonction(cmbFonctions.Text, txtA.Text.Trim, txtB.Text.Trim)
Catch ex As Exception
' ağ hatası oluştu
txtErreur.Text = ex.Message
' bağlantı kapatılıyor
btnFermer_Click(Nothing, Nothing)
End Try
End Sub
Private Sub txtA_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtA.TextChanged
' A değerinin değiştirilmesi
btnCalculer.Enabled = txtA.Text.Trim <> "" And txtB.Text.Trim <> ""
End Sub
Private Sub txtB_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtB.TextChanged
' B değerinde değişiklik
txtA_TextChanged(Nothing, Nothing)
End Sub
' ana yöntem
Public Shared Sub main()
Application.Run(New FormClientSOAP)
End Sub
End Class
Burada da clientSOAP sınıfı, uygulamanın tüm ağ ile ilgili kısımlarını gizlemektedir. Uygulama şu şekilde oluşturulmuştur:
- clientSOAP sınıfını içeren clientSOAP.dll derlemesi proje klasörüne yerleştirilmiştir
- clientsoapgui.vb grafik arayüzü, VS.NET ile oluşturuldu ve ardından bir DOS penceresinde derlendi:
dos>vbc /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll /r:clientSOAP.dll clientsoapgui.vb
dos>dir
04/03/2004 09:13 7 168 clientSOAP.dll
04/03/2004 16:44 9 866 clientsoapgui.vb
04/03/2004 16:44 11 264 clientsoapgui.exe
Ardından grafik kullanıcı arayüzü şu komutla başlatıldı:
10.8. Bir proxy istemcisi
Az önce yapılanları hatırlayalım. Aşağıdaki şemaya göre, bir istemci ile bir web hizmeti arasındaki ağ iletişimini kapsülleyen bir ara sınıf oluşturduk:
![]() |
.NET platformu bu mantığı daha da ileriye götürür. Ulaşılacak web hizmeti belirlendiğinde, web hizmetinin işlevlerine erişmek için aracı olarak kullanacağımız ve tüm ağ kısmını gizleyecek sınıfı otomatik olarak oluşturabiliriz. Bu sınıfa, oluşturulduğu web hizmeti için bir proxy denir.
Bir web hizmetinin proxy sınıfı nasıl oluşturulur? Bir web hizmeti her zaman XML formatında bir açıklama dosyasıyla birlikte gelir. operations web hizmetimizin URI dosyası http://localhost/operations/operations.asmx ise, açıklama dosyası, aşağıdaki ekran görüntüsünde gösterildiği gibi http://localhost/operations/operations.asmx?wsdl adresinde URL olarak bulunur:

Burada, web hizmetinin tüm işlevlerini, her birinin parametre türü ve sayısı ile sonuç türünü ayrıntılı olarak açıklayan bir XML dosyası bulunmaktadır. Bu dosyaya, WSDL (Web Hizmetleri Tanımlama Dili) dilini kullandığı için hizmetin WSDL dosyası denir. Bu dosyadan yola çıkarak, wsdl aracı kullanılarak bir proxy sınıfı oluşturulabilir:
dos>wsdl http://localhost/operations/operations.asmx?wsdl /language=vb
Microsoft (R) Web Services Description Language Utility
[Microsoft (R) .NET Framework, Version 1.1.4322.573]
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
Écriture du fichier 'D:\data\devel\vbnet\poly\chap9\clientproxy\operations.vb'.
dos>dir
04/03/2004 17:17 6 663 operations.vb
wsdl aracı, web hizmetini uygulayan sınıfın adını taşıyan bir VB.NET kaynak dosyası oluşturur (/language=vb seçeneği ile); bu örnekte operations. Oluşturulan kodun bir kısmını inceleyelim:
'------------------------------------------------------------------------------
' <otomatik olarak oluşturulmuş>
' Bu kod bir araç tarafından oluşturulmuştur.
' Çalışma Zamanı Sürümü: 1.1.4322.573
'
' Bu dosyada yapılan değişiklikler hatalı çalışmaya neden olabilir ve
' kod yeniden oluşturulursa kaybolacaktır.
' </otomatik olarak oluşturulmuş>
'------------------------------------------------------------------------------
Option Strict Off
Option Explicit On
Imports System
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Xml.Serialization
'
'Bu kaynak kodu, wsdl tarafından otomatik olarak tégénéré oluşturulmuştur, Sürüm=1.1.4322.573.
'
'<remarks/>
<System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Web.Services.WebServiceBindingAttribute(Name:="operationsSoap", [Namespace]:="st.istia.univ-angers.fr")> _
Public Class operations
Inherits System.Web.Services.Protocols.SoapHttpClientProtocol
'<remarks/>
Public Sub New()
MyBase.New
Me.Url = "http://localhost/operations/operations.asmx"
End Sub
'<remarks/>
<System.Web.Services.Protocols.SoapDocumentMethodAttribute("st.istia.univ-angers.fr/ajouter", RequestNamespace:="st.istia.univ-angers.fr", ResponseNamespace:="st.istia.univ-angers.fr", Use:=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle:=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)> _
Public Function ajouter(ByVal a As Double, ByVal b As Double) As Double
Dim results() As Object = Me.Invoke("ajouter", New Object() {a, b})
Return CType(results(0),Double)
End Function
'<açıklamalar/>
Public Function Beginajouter(ByVal a As Double, ByVal b As Double, ByVal callback As System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult
Return Me.BeginInvoke("ajouter", New Object() {a, b}, callback, asyncState)
End Function
'<açıklamalar/>
Public Function Endajouter(ByVal asyncResult As System.IAsyncResult) As Double
Dim results() As Object = Me.EndInvoke(asyncResult)
Return CType(results(0),Double)
End Function
....
Bu kod ilk bakışta biraz karmaşık görünebilir. Ancak onu kullanabilmek için ayrıntılarını anlamamız gerekmez. Öncelikle sınıf tanımına bir göz atalım:
Sınıf, oluşturulduğu web hizmetinin adı olan operations adını taşır. SoapHttpClientProtocol sınıfından türetilmiştir:

Proxy sınıfımızın tek bir oluşturucusu vardır:
Oluşturucu, url özniteliğine, proxy ile ilişkili web hizmetinin URL değerini atar. Yukarıdaki operations sınıfı, url özniteliğini kendisi tanımlamaz. Bu öznitelik, proxy'nin türetildiği sınıf olan System.Web.Services.Protocols.SoapHttpClientProtocol'ten miras alınmıştır. Şimdi ajouter yöntemiyle ilgili bilgileri inceleyelim:
Public Function ajouter(ByVal a As Double, ByVal b As Double) As Double
Dim results() As Object = Me.Invoke("ajouter", New Object() {a, b})
Return CType(results(0),Double)
End Function
Görüldüğü üzere, bu yöntem operations web hizmetindekiyle aynı imzaya sahiptir ve orada şu şekilde tanımlanmıştır:
<WebMethod> _
Function ajouter(a As Double, b As Double) As Double
Return a + b
End Function 'ajouter
Bu sınıfın web hizmeti ile nasıl iletişim kurduğu burada gösterilmemiştir. Bu iletişim, tamamen üst sınıf olan System.Web.Services.Protocols.SoapHttpClientProtocol tarafından desteklenmektedir. Proxy'de, onu diğer proxy'lerden ayıran tek şey şudur:
- ilişkili web hizmetinin URL'i
- ilişkili hizmetin yöntem tanımları.
operations web hizmetinin yöntemlerini kullanmak için bir istemcinin, yalnızca daha önce oluşturulan operations proxy sınıfına ihtiyacı vardır. Bu sınıfı assembly dosyasında derleyelim:
Şimdi bir konsol istemcisi yazalım. Bu istemci parametre olmadan çağrılır ve klavyeden girilen istekleri yürütür:
dos>testclientproxy
Tapez vos commandes au format : [ajouter|soustraire|multiplier|diviser|toutfaire] a b
ajouter 4 5
résultat=9
soustraire 9 8
résultat=1
multiplier 10 4
résultat=40
diviser 6 7
résultat=0,857142857142857
toutfaire 10 20
résultats=[30,-10,200,0,5]
diviser 5 0
résultat=+Infini
fin
Müşteri kodu şöyledir:
' ad alanları
Imports System
Imports System.IO
Imports System.Text.RegularExpressions
Imports System.Collections
Imports Microsoft.VisualBasic
Public Module testClientProxy
' klavyeden girilen komutları etkileşimli olarak yürütür
' ve bunları operations web hizmetine gönderir
Public Sub Main()
' artık argüman yok - web hizmetinin URL değeri proxy'de sabit olarak kodlanmıştır
' web hizmetinin işlevlerinden oluşan bir sözlük oluşturulur
Dim fonctions As String() = {"ajouter", "soustraire", "multiplier", "diviser", "toutfaire"}
Dim dicoFonctions As New Hashtable
Dim i As Integer
For i = 0 To fonctions.Length - 1
dicoFonctions.Add(fonctions(i), True)
Next i
' bir operations proxy nesnesi oluşturuluyor
Dim myOperations As operations = Nothing
Try
myOperations = New operations
Catch ex As Exception
' bağlantı hatası
erreur("L'erreur suivante s'est produite lors de la connexion au proxy dy service web : " + ex.Message, 2)
End Try
' kullanıcının istekleri klavyeyle giriliyor
' a b biçiminde işlevler - "end" komutuyla sonlandırılır
Dim commande As String = Nothing ' commande tapée au clavier
Dim champs As String() = Nothing ' champs d'une ligne de commande
' kullanıcıya komut istemi
Console.Out.WriteLine("Tapez vos commandes au format : [ajouter|soustraire|multiplier|diviser|toutfaire] a b" + ControlChars.Lf)
' bazı yerel veriler
Dim erreurCommande As Boolean
Dim fonction As String
Dim a, b As Double
' klavyeden girilen komutların işlendiği döngü
While True
' başlangıçta hata yok
erreurCommande = False
' komut okuma
commande = Console.In.ReadLine().Trim().ToLower()
' bitti mi?
If commande Is Nothing Or commande = "fin" Then
Exit While
End If
' komutun alanlara ayrılması
champs = Regex.Split(commande, "\s+")
Try
' üç alan gereklidir
If champs.Length <> 3 Then
Throw New Exception
End If
' 0 numaralı alan tanınan bir işlev olmalıdır
fonction = champs(0)
If Not dicoFonctions.ContainsKey(fonction) Then
Throw New Exception
End If
' 1. alan geçerli bir sayı olmalıdır
a = Double.Parse(champs(1))
' 2. alan geçerli bir sayı olmalıdır
b = Double.Parse(champs(2))
Catch
' komut geçersiz
Console.Out.WriteLine("syntaxe : [ajouter|soustraire|multiplier|diviser] a b")
erreurCommande = True
End Try
' web hizmetine istekte bulunuluyor
If Not erreurCommande Then
Try
Dim résultat As Double
Dim résultats() As Double
If fonction = "ajouter" Then
résultat = myOperations.ajouter(a, b)
Console.Out.WriteLine(("résultat=" + résultat.ToString))
End If
If fonction = "soustraire" Then
résultat = myOperations.soustraire(a, b)
Console.Out.WriteLine(("résultat=" + résultat.ToString))
End If
If fonction = "multiplier" Then
résultat = myOperations.multiplier(a, b)
Console.Out.WriteLine(("résultat=" + résultat.ToString))
End If
If fonction = "diviser" Then
résultat = myOperations.diviser(a, b)
Console.Out.WriteLine(("résultat=" + résultat.ToString))
End If
If fonction = "toutfaire" Then
résultats = myOperations.toutfaire(a, b)
Console.Out.WriteLine(("résultats=[" + résultats(0).ToString + "," + résultats(1).ToString + "," + _
résultats(2).ToString + "," + résultats(3).ToString + "]"))
End If
Catch e As Exception
Console.Out.WriteLine(("L'erreur suivante s'est produite : " + e.Message))
End Try
End If
End While
End Sub
' hataların görüntülenmesi
Public Sub erreur(ByVal msg As String, ByVal exitCode As Integer)
' hata görüntüleniyor
System.Console.Error.WriteLine(msg)
' hata nedeniyle durduruldu
Environment.Exit(exitCode)
End Sub
End Module
Yalnızca proxy sınıfının kullanımına özgü kodu inceliyoruz. Öncelikle bir operations proxy nesnesi oluşturulur:
' bir proxy operations nesnesi oluşturuluyor
Dim myOperations As operations = Nothing
Try
myOperations = New operations
Catch ex As Exception
' bağlantı hatası
erreur("L'erreur suivante s'est produite lors de la connexion au proxy dy service web : " + ex.Message, 2)
End Try
a ve b fonksiyon satırları klavyeden girilir. Bu bilgilere dayanarak, proxy'nin uygun yöntemleri çağrılır:
' web hizmetine istek gönderiliyor
If Not erreurCommande Then
Try
Dim résultat As Double
Dim résultats() As Double
If fonction = "ajouter" Then
résultat = myOperations.ajouter(a, b)
Console.Out.WriteLine(("résultat=" + résultat.ToString))
End If
If fonction = "soustraire" Then
résultat = myOperations.soustraire(a, b)
Console.Out.WriteLine(("résultat=" + résultat.ToString))
End If
If fonction = "multiplier" Then
résultat = myOperations.multiplier(a, b)
Console.Out.WriteLine(("résultat=" + résultat.ToString))
End If
If fonction = "diviser" Then
résultat = myOperations.diviser(a, b)
Console.Out.WriteLine(("résultat=" + résultat.ToString))
End If
If fonction = "toutfaire" Then
résultats = myOperations.toutfaire(a, b)
Console.Out.WriteLine(("résultats=[" + résultats(0).ToString + "," + résultats(1).ToString + "," + _
résultats(2).ToString + "," + résultats(3).ToString + "]"))
End If
Catch e As Exception
Console.Out.WriteLine(("L'erreur suivante s'est produite : " + e.Message))
End Try
Burada, dört işlemi gerçekleştiren toutfaire işlemini ilk kez işliyoruz. Bu işlem, şimdiye kadar göz ardı edilmişti çünkü, tek bir sonuç veren diğer işlevlerin basit XML yanıtlarından daha yönetilmesi zor olan XML zarfına kapsüllenmiş bir sayı dizisi gönderiyordu. Görüldüğü gibi, burada proxy sınıfı kullanıldığında, toutfaire yöntemini kullanmak diğer yöntemleri kullanmaktan daha karmaşık değildir. Uygulama, bir DOS penceresinde şu şekilde derlenmiştir:
dos>vbc /r:operations.dll /r:system.dll /r:system.web.services.dll testClientProxy.vb
dos>dir
04/03/2004 17:17 6 663 operations.vb
04/03/2004 17:24 7 680 operations.dll
04/03/2004 17:41 4 099 testClientProxy.vb
04/03/2004 17:41 5 632 testClientProxy.exe
10.9. Bir Web Hizmetini Yapılandırma
Bir web hizmetinin düzgün bir şekilde başlatılabilmesi için yapılandırma bilgilerine ihtiyaç duyabilir. IIS sunucusunda bu bilgiler, web hizmetiyle aynı klasörde bulunan web.config adlı bir dosyaya yerleştirilebilir. Başlatılması için iki bilgiye ihtiyaç duyan bir web hizmeti oluşturmak istediğimizi varsayalım: bir isim ve bir yaş. Bu iki bilgi, web.config dosyasına aşağıdaki biçimde yerleştirilebilir:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appSettings>
<add key="nom" value="tintin"/>
<add key="age" value="27"/>
</appSettings>
</configuration>
Başlatma parametreleri XML zarfına yerleştirilir:
P adlı ve V değerine sahip bir başlatma parametresi şu satırla tanımlanacaktır:
<add key="P" value="V"/>
Web hizmeti bu bilgileri nasıl alır? IIS bir web hizmetini yüklediğinde, aynı klasörde web.config adlı bir dosya olup olmadığına bakar. Varsa, bu dosyayı okur. P parametresinin V değeri şu komutla elde edilir:
burada ConfigurationSettings, System.Configuration ad alanında bulunan bir sınıftır.
Bu tekniği aşağıdaki web hizmetinde deneyelim:
<%@ WebService language="VB" class=personne %>
Imports System.Web.Services
imports System.Configuration
<WebService([Namespace] := "st.istia.univ-angers.fr")> _
Public Class personne
Inherits WebService
' özellikler
Private nom As String
Private age As Integer
' oluşturucu
Public Sub New()
' özelliklerin başlatılması
nom = ConfigurationSettings.AppSettings("nom")
age = Integer.Parse(ConfigurationSettings.AppSettings("age"))
End Sub
<WebMethod> _
Function id() As String
Return "[" + nom + "," + age.ToString + "]"
End Function
End Class
personne web hizmeti, nom ve age adlı iki özniteliğe sahiptir; bu öznitelikler, web.config yapılandırma dosyasından okunan değerler kullanılarak, parametresiz yapıcıda başlatılır.HTMLP002667ZQX hizmetinin web.config yapılandırma dosyasından okunan değerler kullanılarak parametresiz yapıcıda başlatılır. Bu dosya şöyledir:
<configuration>
<appSettings>
<add key="nom" value="tintin"/>
<add key="age" value="27"/>
</appSettings>
</configuration>
Ayrıca, web hizmetinin parametre içermeyen bir <WebMethod> kimliği vardır ve bu kimlik yalnızca nom ve age özniteliklerini döndürür. Hizmet, yapılandırma dosyasıyla birlikte c:\inetpub\wwwroot\st\personne klasöründe bulunan personne.asmx kaynak dosyasında kayıtlıdır:
Yukarıdaki fiziksel klasöre IIS /config adlı bir sanal klasör atayalım. IIS dosyasını çalıştıralım ve ardından bir tarayıcıdan personne hizmetinin http://localhost/config/personne.asmx adresini açalım:

Tek yöntem kimliği olan bağlantıyı takip edelim:

id yönteminin herhangi bir parametresi yoktur. Appeler düğmesini kullanalım:

Hizmetin web.config dosyasına yerleştirilen bilgileri başarıyla aldık.
10.10. IMPOTS Web hizmeti
Artık hepimizin iyi bildiği IMPOTS uygulamasını ele alıyoruz. Bu uygulamayla en son çalıştığımızda, onu internet üzerinden çağrılabilen bir uzak sunucu haline getirmiştik. Şimdi ise onu bir web hizmeti haline getiriyoruz.
10.10.1. Web hizmeti
Veritabanları bölümünde oluşturduğumuz ve ODBC veritabanındaki bilgilerden oluşturulan impôt sınıfından yola çıkıyoruz:
' seçenekler
Option Strict On
Option Explicit On
' ad alanları
Imports System
Imports System.Data
Imports Microsoft.Data.Odbc
Imports System.Collections
Public Class impôt
' vergi hesaplaması için gerekli veriler
' harici bir kaynaktan gelir
Private limites(), coeffR(), coeffN() As Decimal
' oluşturucu
Public Sub New(ByVal LIMITES() As Decimal, ByVal COEFFR() As Decimal, ByVal COEFFN() As Decimal)
' 3 tablonun da aynı boyutta olup olmadığı kontrol edilir
Dim OK As Boolean = LIMITES.Length = COEFFR.Length And LIMITES.Length = COEFFN.Length
If Not OK Then
Throw New Exception("Les 3 tableaux fournis n'ont pas la même taille(" & LIMITES.Length & "," & COEFFR.Length & "," & COEFFN.Length & ")")
End If
' sorun yok
Me.limites = LIMITES
Me.coeffR = COEFFR
Me.coeffN = COEFFN
End Sub
' 2. oluşturucu
Public Sub New(ByVal DSNimpots As String, ByVal Timpots As String, ByVal colLimites As String, ByVal colCoeffR As String, ByVal colCoeffN As String)
' coeffR, coeffN adlı üç sınır tablosunu
' ODBC veritabanındaki Timpots tablosunun içeriğinden DSNimpots
' colLimites, colCoeffR, colCoeffN bu tablonun üç sütunudur
' bir istisna oluşturabilir
Dim connectString As String = "DSN=" + DSNimpots + ";" ' chaîne de connexion à la base
Dim impotsConn As OdbcConnection = Nothing ' la connexion
Dim sqlCommand As OdbcCommand = Nothing ' la commande SQL
' SELECT sorgusu
Dim selectCommand As String = "select " + colLimites + "," + colCoeffR + "," + colCoeffN + " from " + Timpots
' verileri almak için tablolar
Dim tLimites As New ArrayList
Dim tCoeffR As New ArrayList
Dim tCoeffN As New ArrayList
' veritabanına erişilmeye çalışılır
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
' bir command nesnesi oluşturuluyor
sqlCommand = New OdbcCommand(selectCommand, impotsConn)
' sorgu yürütülüyor
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Alınan tablonun işlenmesi
While myReader.Read()
' geçerli satırın verileri tablolara aktarılıyor
tLimites.Add(myReader(colLimites))
tCoeffR.Add(myReader(colCoeffR))
tCoeffN.Add(myReader(colCoeffN))
End While
' Kaynaklar serbest bırakılır
myReader.Close()
impotsConn.Close()
' Dinamik tablolar statik tablolara aktarılır
Me.limites = New Decimal(tLimites.Count) {}
Me.coeffR = New Decimal(tLimites.Count) {}
Me.coeffN = New Decimal(tLimites.Count) {}
Dim i As Integer
For i = 0 To tLimites.Count - 1
limites(i) = Decimal.Parse(tLimites(i).ToString())
coeffR(i) = Decimal.Parse(tCoeffR(i).ToString())
coeffN(i) = Decimal.Parse(tCoeffN(i).ToString())
Next i
End Sub
' Vergi hesaplanması
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Integer) As Long
' pay sayısının hesaplanması
Dim nbParts As Decimal
If marié Then
nbParts = CDec(nbEnfants) / 2 + 2
Else
nbParts = CDec(nbEnfants) / 2 + 1
End If
If nbEnfants >= 3 Then
nbParts += 0.5D
End If
' Vergilendirilebilir gelir ve aile katsayısı hesaplaması
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' vergi hesaplaması
limites((limites.Length - 1)) = QF + 1
Dim i As Integer = 0
While QF > limites(i)
i += 1
End While
' sonuç döndürme
Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
End Function
End Class
Web hizmetinde yalnızca parametresiz bir oluşturucu kullanılabilir. Bu nedenle sınıfın oluşturucusu şu şekilde olacaktır:
' oluşturucu
Public Sub New()
' coeffR, coeffN adlı üç sınır tablosunu
' ODBC veritabanındaki Timpots tablosunun içeriğinden DSNimpots
' colLimites, colCoeffR, colCoeffN bu tablonun üç sütunudur
' bir istisna oluşturabilir
' hizmetin yapılandırma parametreleri alınır
Dim DSNimpots As String = ConfigurationSettings.AppSettings("DSN")
Dim Timpots As String = ConfigurationSettings.AppSettings("TABLE")
Dim colLimites As String = ConfigurationSettings.AppSettings("COL_LIMITES")
Dim colCoeffR As String = ConfigurationSettings.AppSettings("COL_COEFFR")
Dim colCoeffN As String = ConfigurationSettings.AppSettings("COL_COEFFN")
' veritabanını işler
Dim connectString As String = "DSN=" + DSNimpots + ";" ' chaîne de connexion à la base
Önceki sınıfın oluşturucusundaki beş parametre artık hizmetin web.config dosyasında okunmaktadır. impots.asmx kaynak dosyasının kodu aşağıdaki gibidir. Bu kod, önceki kodun büyük bir kısmını içermektedir. Biz sadece web hizmetine özgü kod bölümlerini çerçevelemeyle yetindik:
<%@ WebService language="VB" class=impots %>
' vergi web hizmeti oluşturulur
Imports System
Imports System.Data
Imports Microsoft.Data.Odbc
Imports System.Collections
Imports System.Configuration
Imports System.Web.Services
<WebService([Namespace]:="st.istia.univ-angers.fr")> _
Public Class impôt
Inherits WebService
' vergi hesaplaması için gerekli veriler
' harici bir kaynaktan sağlanır
Private limites(), coeffR(), coeffN() As Decimal
Private OK As Boolean = False
Private errMessage As String = ""
' oluşturucu
Public Sub New()
' coeffR ve coeffN adlı üç sınır tablosunu
' ODBC veritabanındaki Timpots tablosunun içeriğinden DSNimpots
' colLimites, colCoeffR, colCoeffN bu tablonun üç sütunudur
' bir istisna oluşturabilir
' hizmetin yapılandırma parametreleri alınır
Dim DSNimpots As String = ConfigurationSettings.AppSettings("DSN")
Dim Timpots As String = ConfigurationSettings.AppSettings("TABLE")
Dim colLimites As String = ConfigurationSettings.AppSettings("COL_LIMITES")
Dim colCoeffR As String = ConfigurationSettings.AppSettings("COL_COEFFR")
Dim colCoeffN As String = ConfigurationSettings.AppSettings("COL_COEFFN")
' veritabanı işlenir
Dim connectString As String = "DSN=" + DSNimpots + ";" ' chaîne de connexion à la base
Dim impotsConn As OdbcConnection = Nothing ' la connexion
Dim sqlCommand As OdbcCommand = Nothing ' la commande SQL
Dim myReader As OdbcDataReader ' lecteur de données Odbc
' SELECT sorgusu
Dim selectCommand As String = "select " + colLimites + "," + colCoeffR + "," + colCoeffN + " from " + Timpots
' verileri almak için tablolar
Dim tLimites As New ArrayList
Dim tCoeffR As New ArrayList
Dim tCoeffN As New ArrayList
' veritabanına erişilmeye çalışılır
Try
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
' bir komut nesnesi oluşturuluyor
sqlCommand = New OdbcCommand(selectCommand, impotsConn)
' sorgu yürütülüyor
myReader = sqlCommand.ExecuteReader()
' Alınan tablonun işlenmesi
While myReader.Read()
' geçerli satırın verileri tablolara aktarılıyor
tLimites.Add(myReader(colLimites))
tCoeffR.Add(myReader(colCoeffR))
tCoeffN.Add(myReader(colCoeffN))
End While
' Kaynaklar serbest bırakılır
myReader.Close()
impotsConn.Close()
' Dinamik tablolar statik tablolara aktarılır
Me.limites = New Decimal(tLimites.Count) {}
Me.coeffR = New Decimal(tLimites.Count) {}
Me.coeffN = New Decimal(tLimites.Count) {}
Dim i As Integer
For i = 0 To tLimites.Count - 1
limites(i) = Decimal.Parse(tLimites(i).ToString())
coeffR(i) = Decimal.Parse(tCoeffR(i).ToString())
coeffN(i) = Decimal.Parse(tCoeffN(i).ToString())
Next i
' Tamam
OK = True
errMessage = ""
Catch ex As Exception
' hata
OK = False
errMessage += "[" + ex.Message + "]"
End Try
End Sub
' vergi hesaplaması
<WebMethod()> _
Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Integer) As Long
' pay sayısının hesaplanması
Dim nbParts As Decimal
If marié Then
nbParts = CDec(nbEnfants) / 2 + 2
Else
nbParts = CDec(nbEnfants) / 2 + 1
End If
If nbEnfants >= 3 Then
nbParts += 0.5D
End If
' vergilendirilebilir gelir ve aile katsayısı hesaplaması
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' vergi hesaplaması
limites((limites.Length - 1)) = QF + 1
Dim i As Integer = 0
While QF > limites(i)
i += 1
End While
' sonuç döndürme
Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
End Function
' kimlik
<WebMethod()> _
Function id() As String
' her şeyin yolunda olup olmadığını kontrol etmek için OK
Return "[" + OK + "," + errMessage + "]"
End Function
End Class
impots sınıfında, onu bir web hizmeti haline getirmek için gerekli olan değişikliklerin dışında yapılan birkaç değişikliği açıklayalım:
- yapıcıda veritabanının okunması başarısız olabilir. Bu nedenle sınıfımıza iki öznitelik ve bir yöntem ekledik:
- Veritabanı okunabilmişse OK boole değeri vrai olur, aksi takdirde faux olur
- Veritabanı okunamadıysa, errMessage dizesi bir hata mesajı içerir.
- Parametre içermeyen id yöntemi, bu iki özniteliğin değerini almayı sağlar.
- Veritabanına erişimde meydana gelebilecek hataları yönetmek için, bu erişimle ilgili oluşturucu kodunun ilgili kısmı bir try-catch ile çevrelenmiştir.
Hizmetin yapılandırma dosyası web.config şu şekildedir:
<configuration>
<appSettings>
<add key="DSN" value="mysql-impots" />
<add key="TABLE" value="timpots" />
<add key="COL_LIMITES" value="limites" />
<add key="COL_COEFFR" value="coeffr" />
<add key="COL_COEFFN" value="coeffn" />
</appSettings>
</configuration>
impots hizmetini ilk kez yüklemeye çalışıldığında, derleyici, yönergede kullanılan Microsoft.Data.Odbc ad alanını bulamadığını bildirdi:
Belgelere başvurulduktan sonra
- web.config dosyasına, Microsoft.Data.odbc derleme biriminin kullanılması gerektiğini belirten bir derleme yönergesi eklendi
- microsoft.data.odbc.dll dosyasının bir kopyası, projenin bin klasörüne yerleştirildi. Bu klasör, bir web hizmeti derleyicisi "assembly" ararken sistematik olarak taranır.
Başka çözümler de mümkün görünse de burada ayrıntılı olarak ele alınmamıştır. Dolayısıyla yapılandırma dosyası şu hale gelmiştir:
<configuration>
<appSettings>
<add key="DSN" value="mysql-impots" />
<add key="TABLE" value="timpots" />
<add key="COL_LIMITES" value="limites" />
<add key="COL_COEFFR" value="coeffr" />
<add key="COL_COEFFN" value="coeffn" />
</appSettings>
<system.web>
<compilation>
<assemblies>
<add assembly="Microsoft.Data.Odbc" />
</assemblies>
</compilation>
</system.web>
</configuration>
impots\bin dosyasının içeriği:
Hizmet ve yapılandırma dosyası impots dosyasına yerleştirildi:
Web hizmetinin fiziksel klasörü, IIS'teki /impots sanal klasörüyle ilişkilendirilmiştir. Hizmetin sayfası şu şekildedir:

id bağlantısını takip edersek:

Appeler düğmesini kullanırsak:

Yukarıdaki sonuç, OK (true) ve errMessage ("") özniteliklerinin değerlerini gösterir. Bu örnekte, veritabanı başarıyla yüklenmiştir. Bu her zaman böyle olmamıştır ve bu nedenle hata mesajına erişebilmek için id yöntemini ekledik. Hata, veritabanının DSN adının kullanıcı olarak DSN olarak tanımlanmış olmasıydı; oysa bunun sistem olarak DSN olarak tanımlanması gerekiyordu. Bu ayrım, 32 bit ODBC kaynak yöneticisinde yapılır:
![]() |
Hizmet sayfasına geri dönelim:

calculer bağlantısını takip edelim:

Çağrı parametrelerini tanımlayıp çağrıyı gerçekleştiriyoruz:

Sonuç doğru.
10.10.2. impots hizmetinin proxy'sini oluşturma
Artık çalışır durumda bir impots web hizmetimiz olduğuna göre, bunun proxy sınıfını oluşturabiliriz. Bu sınıfın, istemci uygulamaları tarafından impots web hizmetine şeffaf bir şekilde erişmek için kullanılacağını hatırlatırız. Öncelikle wsdl yardımcı programını kullanarak proxy sınıfının kaynak dosyasını oluşturuyoruz, ardından bu dosya bir DLL olarak derleniyor.
dos>wsdl /language=vb http://localhost/impots/impots.asmx
Microsoft (R) Web Services Description Language Utility
[Microsoft (R) .NET Framework, Version 1.1.4322.573]
Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.
Écriture du fichier 'D:\data\serge\devel\vbnet\poly\chap9\impots\impots.vb'.
D:\data\serge\devel\vbnet\poly\chap9\impots>dir
09/03/2004 10:20 <REP> bin
09/03/2004 10:58 4 651 impots.asmx
09/03/2004 11:05 3 364 impots.vb
09/03/2004 10:19 431 web.config
dos>vbc /t:library /r:system.dll /r:system.web.services.dll /r:system.xml.dll impots.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4
pour Microsoft (R) .NET Framework version 1.1.4322.573
Copyright (C) Microsoft Corporation 1987-2002. Tous droits réservés.
dos>dir
09/03/2004 10:20 <REP> bin
09/03/2004 10:58 4 651 impots.asmx
09/03/2004 11:09 5 120 impots.dll
09/03/2004 11:05 3 364 impots.vb
09/03/2004 10:19 431 web.config
10.10.3. Proxy'yi bir istemciyle kullanma
Veritabanları ile ilgili bölümde, vergi hesaplamasına olanak tanıyan bir konsol uygulaması oluşturmuştuk:
dos>dir
27/02/2004 16:56 5 120 impots.dll
27/02/2004 17:12 3 586 impots.vb
27/02/2004 17:08 6 144 testimpots.exe
27/02/2004 17:18 3 328 testimpots.vb
dos>testimpots
pg DSNimpots tabImpots colLimites colCoeffR colCoeffN
dos>testimpots odbc-mysql-dbimpots impots limites coeffr coeffn
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 200000
impôt=22504 F
testimpots programı, o zamanlar impots.dll dosyasında bulunan klasik impôt sınıfını kullanıyordu. testimpots.vb programının kodu şöyleydi:
Option Explicit On
Option Strict On
' ad alanları
Imports System
Imports Microsoft.VisualBasic
' test sayfası
Module testimpots
Sub Main(ByVal arguments() As String)
' etkileşimli vergi hesaplama programı
' kullanıcı klavyeden üç veri girer: evli nbEnfants maaş
' program daha sonra ödenecek vergiyi gösterir
Const syntaxe1 As String = "pg DSNimpots tabImpots colLimites colCoeffR colCoeffN"
Const syntaxe2 As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' program ayarlarının kontrolü
If arguments.Length <> 5 Then
' hata mesajı
Console.Error.WriteLine(syntaxe1)
' son
Environment.Exit(1)
End If 'if
' argümanlar alınır
Dim DSNimpots As String = arguments(0)
Dim tabImpots As String = arguments(1)
Dim colLimites As String = arguments(2)
Dim colCoeffR As String = arguments(3)
Dim colCoeffN As String = arguments(4)
' vergi nesnesi oluşturuluyor
Dim objImpôt As impôt = Nothing
Try
objImpôt = New impôt(DSNimpots, tabImpots, colLimites, colCoeffR, colCoeffN)
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(2)
End Try
' sonsuz döngü
While True
' başlangıçta hata yok
Dim erreur As Boolean = False
' vergi hesaplaması için parametreler isteniyor
Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
Dim paramètres As String = Console.In.ReadLine().Trim()
' ne yapılmalı?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' giriş satırındaki argüman sayısının kontrolü
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe2)
erreur = True
End If
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Integer
If Not erreur Then
' parametrelerin geçerliliğinin kontrolü
' evli
marié = args(0).ToLower()
If marié <> "o" And marié <> "n" Then
Console.Error.WriteLine((syntaxe2 + ControlChars.Lf + "Argument marié incorrect : tapez o ou n"))
erreur = True
End If
' nbEnfants
nbEnfants = 0
Try
nbEnfants = Integer.Parse(args(1))
If nbEnfants < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument nbEnfants incorrect : tapez un entier positif ou nul")
erreur = True
End Try
' maaş
salaire = 0
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument salaire incorrect : tapez un entier positif ou nul")
erreur = True
End Try
End If
If Not erreur Then
' parametreler doğru - vergi hesaplanıyor
Console.Out.WriteLine(("impôt=" & objImpôt.calculer(marié = "o", nbEnfants, salaire).ToString + " F"))
End If
End While
End Sub
End Module
Aynı programı yeniden ele alıyoruz ve şimdi daha önce oluşturduğumuz impots proxy sınıfı aracılığıyla impots web hizmetini kullanmasını sağlıyoruz. Kodu biraz değiştirmek zorundayız:
- Orijinal impôt sınıfı beş argümanlı bir oluşturucuya sahipken, impots proxy sınıfı argüman içermeyen bir oluşturucuya sahiptir. Gördüğümüz gibi, bu beş parametre artık web hizmetinin yapılandırma dosyasında sabitlenmiştir.
- Bu nedenle, bu beş parametreyi test programına argüman olarak aktarmaya artık gerek kalmamıştır
Yeni kod şu şekildedir:
Imports System
Imports Microsoft.VisualBasic
' test sayfası
Module testimpots
Public Sub Main(ByVal arguments() As String)
' etkileşimli vergi hesaplama programı
' kullanıcı klavyeden üç veri girer: evli nbEnfants maaş
' program daha sonra ödenecek vergiyi gösterir
Const syntaxe2 As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' bir vergi nesnesinin oluşturulması
Dim objImpôt As impôt = Nothing
Try
objImpôt = New impôt
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(2)
End Try
' sonsuz döngü
Dim erreur As Boolean
While True
' başlangıçta hata yok
erreur = False
' vergi hesaplaması için parametreler isteniyor
Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
Dim paramètres As String = Console.In.ReadLine().Trim()
' ne yapılmalı?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' giriş satırındaki argüman sayısının kontrolü
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe2)
erreur = True
End If
If Not erreur Then
' parametrelerin geçerliliğinin kontrolü
' evli
Dim marié As String = args(0).ToLower()
If marié <> "o" And marié <> "n" Then
Console.Error.WriteLine((syntaxe2 + ControlChars.Lf + "Argument marié incorrect : tapez o ou n"))
erreur = True
End If
' nbEnfants
Dim nbEnfants As Integer = 0
Try
nbEnfants = Integer.Parse(args(1))
If nbEnfants < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine((syntaxe2 + ControlChars.Lf + "Argument nbEnfants incorrect : tapez un entier positif ou nul"))
erreur = True
End Try
' maaş
Dim salaire As Integer = 0
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine((syntaxe2 + ControlChars.Lf + "Argument salaire incorrect : tapez un entier positif ou nul"))
erreur = True
End Try
' parametreler doğruysa - vergi hesaplanır
If Not erreur Then Console.Out.WriteLine(("impôt=" + objImpôt.calculer(marié = "o", nbEnfants, salaire).ToString + " F"))
End If
End While
End Sub
End Module
Aynı klasörde impots.dll proxy'si ve testimpots kaynak dosyası bulunmaktadır.
dos>dir
09/03/2004 11:28 <REP> bin
09/03/2004 11:09 5 120 impots.dll
09/03/2004 11:34 3 396 testimpots.vb
09/03/2004 10:19 431 web.config
testimpots.vb kaynak kodunu derliyoruz:
dos>vbc /r:impots.dll /r:microsoft.visualbasic.dll /r:system.web.services.dll /r:system.dll testimpots.vb
dos>dir
09/03/2004 11:28 <REP> bin
09/03/2004 11:09 5 120 impots.dll
09/03/2004 11:05 3 364 impots.vb
09/03/2004 11:35 5 632 testimpots.exe
09/03/2004 11:34 3 396 testimpots.vb
09/03/2004 10:19 431 web.config
ardından çalıştıralım:
dos>testimpots
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 200000
impôt=22504 F
Beklediğimiz sonucu elde ediyoruz.





