https://www.buraksenyurt.com/Burak Selim Şenyurt - WCF Eco System2017-01-18T06:22:41+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://www.buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://www.buraksenyurt.com/post/Silverlight-JSON-ile-CalismakSilverlight - JSON ile Çalışmak2010-08-13T01:15:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f4%2fblg177_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Uzun süredir şöyle deliksiz uyuyamıyordum. Malum evde bir afacan var. Pek uyumayı sevmeyen, sürekli hareket halinde olmak isteyen S(h)arp Efe izin verdiğinde, eşim ve ben dinlenmek için çeşitli işlere dalıyoruz. Ben uzun süredir <strong>Bulmacalara </strong>takılmış durumdayım. Bir de şu eski dil karşılıklarını isteyen sorular olmasa. Geçtiğimiz günlerde yine böyle bir boşluk yakalamışken, kendimi bulmacalar arasında yüzerken buluverdim. Ancak bir süre sonra "...eski dildeki karşılığı..." sorularından sıkıldım ve televizyonda neler olduğuna bir akayım dedim.</p>
<p>Televizyonda yandaki resimde görülen adam vardı ve ismi <strong>Jason</strong>' dı. Açıkçası <strong>Jason Statham</strong>' ın fanatiği bir sinemasever olarak bu isim benzerliğinin, böyle korkutucu bir karakter üzerinde olması beni üzmüştü. Nitekim Jason ismini düşününce aklıma gıcır gıcır parlayan <strong>Audi </strong>marka arabalar gelmekteydi. Her neyse...Filme fazla takılmadım ama <strong>Jason, Jason</strong> derken, bu isim <strong>JSON </strong>diye dudaklarımdan süzülmeye başladı. Pek tabi bunun doğal sonucu olarak bilgisayarımın başına oturdum ve <strong>JSON </strong>ile ilişkili bir şeyler yazmaya karar verdim. İşte başlıyoruz <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /></p>
<p>Bildiğiniz üzere <strong>HTTP </strong>bazlı <strong>WCF </strong>servislerinden(<a class="postheader taggedlink" href="https://www.buraksenyurt.com/admin/app/editor/post/WCF-WebHttp-Services-JSON-Formatli-Response-Uretmek" target="_blank">WCF WebHttp Services - JSON Formatlı Response Üretmek</a>) <strong>JSON(JavaScript Object Notation) </strong>formatında çıktılar yayınlanabilmektedir. Bazı durumlarda istemci tarafı, <strong>JSON </strong>veri içeriği ile çalışmayı tercih edilebilir. Özellikle <strong>XML </strong>ile karşılaştırıldığında, <strong>JSON</strong> formatının daha az yer tutan bir yapıya sahip olması, bu seçimin yapılmasında önemli bir etkendir. Biz bu yazımızda bir <strong>WCF WebHttp Service </strong>tarafından yayınlanan <strong>JSON </strong>formatlı veri çıktısının, örnek bir <strong>Silverlight </strong>istemcisi tarafından nasıl ele alınabileceğini incelemeye çalışıyor olacağız.</p>
<p>Silverlight tarafında <strong>JSON </strong>içeriği ile çalışabilmek adına geliştirilmiş <strong>JsonArray, JsonObject, JsonPrimitive </strong>gibi tipler bulunmaktadır. Bu tipler sayesinde <strong>JSON </strong>veri kümesinde yer alan <strong>string, number, Boolean </strong>gibi veri türleri kod içerisinde ele alınabilir. Ayrıca tek <strong>JSON </strong>nesnesi veya bir <strong>JSON </strong>nesne listesinin ele alınması da sağlanabilir. Bu geliştiriciler için önemlidir. Nitekim Web ortamında gelen <strong>JSON </strong>içeriğinin <strong>Parse </strong>edilme işlemleri ile uğraşılmasına gerek kalmamaktadır.</p>
<p>Dilerseniz hiç vakit kaybetmeden örnek bir <strong>Silverlight </strong>uygulaması üzerinden ilerlemeye çalışalım. İşe ilk olarak <strong>IIS</strong> üzerinde <strong>host </strong>edeceğimiz<strong> WCF Rest Service Application</strong> projesini ve aşağıdaki kod içeriğine sahip <strong>LogService </strong>servis örneğini geliştirerek başlayabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
namespace TraceLogServiceApplication
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class LogService
{
[WebGet(UriTemplate = "Logs/All",ResponseFormat=WebMessageFormat.Json)]
public List<Log> GetAllLogs()
{
return new List<Log>()
{
new Log{ Source="Sql Server", Content="Sql servisi başlatıldı", IsCritical=false, Level=5},
new Log{ Source="Sql Server", Content="Sql Agent servisinde hata.", IsCritical=true, Level=1},
new Log{ Source="DTC", Content="Dağıtık Transaction nesnesi üretildi.", IsCritical=false, Level=3},
new Log{ Source="WF Runtime", Content="Süreç persist edildi", IsCritical=true, Level=2}
};
}
}
public class Log
{
public string Source { get; set; }
public string Content { get; set; }
public int Level { get; set; }
public bool IsCritical { get; set; }
}
}</pre>
<p><strong>LogService </strong>içerisinde yer alan <strong>GetAllLogs </strong>isimli servis operasyonu <strong>Log </strong>tipinden bir kaç eleman içeren basit bir <strong>List<Log> </strong>koleksiyonunu geriye döndürmektedir. Çalışma zamanında oluşturulacak olan bu içerik, istemci tarafına <strong>JSON </strong>formatında gönderilecektir. Bunun için dikkat edileceği üzere <strong>ResponseFormat </strong>özelliğinin değeri <strong>WebMessageFormat.Json </strong>sabiti olarak belirlenmiştir. Servisimizi bu haliyle test etmek istediğimizde adres satırından <strong>http://localhost:12043/LogService/Logs/All </strong>gibi bir çağrı yapmamız yeterli olacaktır. Bunun sonucunda aşağıdaki <strong>JSON </strong>içeriği üretilecektir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false">[{"Content":"Sql servisi başlatıldı","IsCritical":false,"Level":5,"Source":"Sql Server"},{"Content":"Sql Agent servisinde hata.","IsCritical":true,"Level":1,"Source":"Sql Server"},{"Content":"Dağıtık Transaction nesnesi üretildi.","IsCritical":false,"Level":3,"Source":"DTC"},{"Content":"Süreç persist edildi","IsCritical":true,"Level":2,"Source":"WF Runtime"}]</pre>
<p>Bu işlemin ardından servisi <strong>IIS </strong>alınta <strong>Publish </strong>etmemiz yeterlidir. <strong>Publish </strong>ayarlarını aşağıdaki resimde görüldüğü gibi belirleyebiliriz.</p>
<p><img src="/pics/2010%2f4%2fblg177_PublishProfile.gif" alt="" /></p>
<p>Eğer <strong>Publish </strong>işlemi başarılı olduysa<em>(<strong>IIS </strong>üzerinden ilgili uygulamanın <strong>Web Application </strong>olarak set edilmesine</em><em>-Convert to Application seçeneği dikkat ederekten) </em>herhangibir tarayıcı uygulamadan, <strong>http://localhost/TraceLogServiceApplication/LogService/Logs/All </strong>şeklinde bir çağrıda bulunabiliyor olmamız gerekmektedir ki bu çağrının sonucu olarakta, yukarıdaki <strong>JSON </strong>içeriğine tekrardan ulaşabiliyor olmalıyız.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f4%2fTraceLogServiceApplication.rar">TraceLogServiceApplication.rar (30,47 kb)</a> <strong>[Örnek Visual Studio 2010 Ultimate sürümü üzerinde test edilmiştir]</strong></p>
<p>Tabi yapmamız gereken bir işlem daha bulunmaktadır. Hatırlayacağınız üzere <strong>Silverlight </strong>istemcileri için <strong>Cross-Domain Policy </strong>sorunsalı mevcuttur. Bu nedenle <strong>IIS </strong>üzerinde daha önceki yazılarda değindiğimiz <strong>ClientAccessPolicy.xml </strong>dosyasının içeriğini aşağıdaki gibi düzenlememiz ve <strong>TraceLogServiceApplication </strong>için gerekli <strong>garanti haklarını(grant-to)</strong> belirlememiz gerekmektedir.</p>
<p><img src="/pics/2010%2f4%2fblg177_CAPFile.gif" alt="" /></p>
<p>Artık <strong>Silverlight 4.0</strong> tabanlı istemci uygulamamızı geliştirmeye başlayabiliriz. Bu amaçla, <strong>JsonConsumer </strong>isimli <strong>Silverlight </strong>uygulamamız içerisindeki <strong>MaingPage.xaml </strong>ve kod içerikleri aşağıdaki gibi geliştirilebilir.</p>
<p><strong>MainPage.xaml içeriği;</strong></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><UserControl x:Class="JsonConsumer.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="Get All Logs" Height="23" HorizontalAlignment="Left" Margin="24,20,0,0" Name="GetLogsButton" VerticalAlignment="Top" Width="75" Click="GetLogsButton_Click" />
<sdk:DataGrid AutoGenerateColumns="True" ItemsSource="{Binding}" Height="204" HorizontalAlignment="Left" Margin="24,56,0,0" Name="LogsDataGrid" VerticalAlignment="Top" Width="347"/>
</Grid>
</UserControl></pre>
<p><strong>MainPage.xaml.cs içeriği;</strong></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.IO;
using System.Json;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
// JsonArray, JsonObject gibi tiplerin kullanılabilmesi için Silverlight projesine System.Json.dll assembly' ının referans edilmesi gerekmektedir.
namespace JsonConsumer
{
public partial class MainPage
: UserControl
{
WebClient client = null;
public MainPage()
{
InitializeComponent();
client= new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
}
private void GetLogsButton_Click(object sender, RoutedEventArgs e)
{
client.OpenReadAsync(new Uri("http://localhost/TraceLogServiceApplication/LogService/Logs/All"));
}
void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
Stream responseStream = e.Result;
// JsonArray sınıfının static Load metodu, Http Web servisine yapılan talep sonrası dönen Stream örneğini alır.
// Load metodu JsonValue tipinden bir referans döndürmektedir ve dizi olarak ele alabilmek için JsonArray tipine bilinçli bir dönüşüm yapılmıştır.
JsonArray logs=(JsonArray)JsonArray.Load(responseStream);
// Elde edilen JSON verisinden IsCritical değeri true olanlar çekilir ve LogInfo isimli tip içerisinde toplanır.
var criticialLogs = from log in logs
where log["IsCritical"]
select new LogInfo
{
Content=log["Content"].ToString(),
Source=log["Source"].ToString(),
Level=log["Level"]
};
// Elde edilen veri kümesi DataGrid kontrolüne veri kaynağı olarak gösterilir
LogsDataGrid.DataContext = criticialLogs;
}
}
// Servis tarafındaki Log tipinin istemci tarafındaki karşılığı
public class LogInfo
{
public string Source { get; set; }
public string Content { get; set; }
public int Level { get; set; }
public bool IsCritical { get; set; }
}
}</pre>
<p>Hatırlayacağınız üzere <strong>WCF WebHttp Service</strong> örneklerine yapılacak olan istemci çağrıları için <strong>WebClient </strong>tipinden yararlanılmaktadır. Bu amaçla<strong> Button </strong>kontrolüne basıldığında, asenkron olarak söz konusu servise bir talepte bulunulmaktadır<strong>(OpenReadAsync)</strong>. Talep sonuçlandığında ise geri bildirim olay metodu devreye girmektedir<strong>(OpenReadCompleted)</strong>. İşte bu olay metodu içerisinde <strong>JSON </strong>veri içeriğinin ele alınması için gerekli işlemler gerçekleştirilmektedir.</p>
<p>Bu metoda ait kod parçasındaki en büyük yardımıcı <strong>JsonArray </strong>tipi ve <strong>Load </strong>fonksiyonudur . Bu fonksiyon, parametre olarak <strong>LogService </strong>isimli <strong>WCF WebHttp Servisine </strong>gönderilen talep sonucu, istemci tarafına indirilen <strong>Stream</strong> referansını kullanmaktadır. Sonuç daha sonradan basit bir <strong>LINQ </strong>sorgusu ile değerlendirilmiş ve örnek olarak kritik seviyedeki log bilgilerinin değerlendirilmesi amaçlanmıştır. Uygulamanın çalışma zamanı görüntüsü aşağıdaki gibi olacaktır.</p>
<p><img src="/pics/2010%2f4%2fblg177_Runtime.gif" alt="" /></p>
<p>Görüldüğü gibi <strong>JSON </strong>formatındaki içerik <strong>Silverlight </strong>tarafında başarılı bir şekilde ele alınmış ve <strong>veri bağlı bir kontrol(DataGrid)</strong> ile ilişkilendirilebilmiştir. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f4%2fJsonConsumer.rar">JsonConsumer.rar (1,92 mb)</a> <strong>[Örnek Visual Studio 2010 Ultimate sürümü üzerinde test edilmiştir]</strong></p>2010-08-13T01:15:00+00:00silverlightwcfwcf webhttp servicesjsonbsenyurtSilverlight tarafında JSON içeriği ile çalışabilmek adına geliştirilmiş JsonArray, JsonObject, JsonPrimitive gibi tipler bulunmaktadır. Bu tipler sayesinde JSON veri kümesinde yer alan string, number, Boolean gibi veri türleri kod içerisinde ele alınabilir. Ayrıca tek JSON nesnesi veya bir JSON nesne listesinin ele alınması da sağlanabilir. Bu geliştiriciler için önemlidir. Nitekim Web ortamında gelen JSON içeriğinin Parse edilme işlemleri ile uğraşılmasına gerek kalmamaktadır.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=3c19fb81-50f0-4a9c-91ee-e93549217c2f2https://www.buraksenyurt.com/trackback.axd?id=3c19fb81-50f0-4a9c-91ee-e93549217c2fhttps://www.buraksenyurt.com/post/Silverlight-JSON-ile-Calismak#commenthttps://www.buraksenyurt.com/syndication.axd?post=3c19fb81-50f0-4a9c-91ee-e93549217c2fhttps://www.buraksenyurt.com/post/Silverlight-Tarafindan-Feed-OkumakSilverlight Tarafından Feed Okumak2010-07-22T09:05:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f4%2fblg176_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Yeni bir maceraya hazır mısınız? Hureyyy dediğinizi duyar gibiyim. Bildiğiniz üzere <strong>Internet </strong>kaynaklarının takibinin kolay bir şekilde yapılabilmesi adına <strong>RSS </strong>veya <strong>Atom </strong>formatındaki <strong>Feed </strong>içeriklerinden sıklıkla yararlanmaktayız. <strong>Blog, Community, News Group </strong>ve benzeri pek çok internet kaynağı, güncel içeriklerini yayınlamak amacıyla global olarak standart hale getirilmiş olan bu formatları kullanmaktalar.</p>
<p>Pek tabi yayınlanan bu içeriklerin takip edilebilmesi içinde çeşitli istemci programlar söz konusu. <strong>FeedReader</strong> bu uygulamalara örnek olarak verilebilecek <strong>Windows </strong>tabanlı iddialı programlardan birisi. <strong>Feed </strong>içerikleri zaman zaman <strong>internet </strong>siteleri üzerinde kontrol şeklinde de barındırılmaktadır. Söz gelimi pek çok <strong>blog </strong>içerisinde bu durum söz konusudur ve hatta hazır <strong>Widget</strong>' lar yardımıyla entegrasyonları son derece kolaydır. Peki maceramız nerede başlıyor? Özellikle ambulans resminin bu konu ile alakası nedir? <img title="Sealed" src="/editors/tiny_mce3/plugins/emotions/img/smiley-sealed.gif" alt="Sealed" border="0" /></p>
<p>Doğruyu söylemek gerekirse sıkıldığım bir ara ne yapayım diye düşünürken <strong>Silverlight 4.0</strong> tabanlı olarak geliştirilen bir uygulamadan <strong>RSS </strong>içeriklerini nasıl okuyabileceğimi düşünmeye başladım. Daha önceden <strong>HTTP </strong>bazlı <strong>Get,Post,Put, Delete</strong> metodlarınaa cevap veren <strong>WCF </strong>tabanlı servislerin tüketilmesi için <strong>WebClient </strong>tipinden nasıl yararlanıldığını incelemiştim(<a class="postheader taggedlink" href="https://www.buraksenyurt.com/admin/app/editor/post/Silverlight-Tarafinda-HTTP-Bazli-Servisleri-Kullanmak" target="_blank">Silverlight Tarafında HTTP Bazli Servisleri Kullanmak</a> isimli yazıyı incelemenizi öneririm) Yine aynı şekilde devam ederek herhangibir <strong>RSS </strong>içeriğini örnek <strong>Silverlight </strong>uygulamama taşıyabileceğimi düşünerek kolları sıvadım ve heyecanlı bir şekilde aşağıdaki ekran görüntüsü ve <strong>XAML </strong>içeriğine sahip kontrolü oluşturdum.</p>
<p><img src="/pics/2010%2f4%2fblg176_Design.gif" alt="" /></p>
<p>XAML içeriği;</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><UserControl x:Class="RSSReaderim.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="337" d:DesignWidth="394" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="RSS Oku" Height="23" HorizontalAlignment="Left" Margin="313,76,0,0" Name="ReadRSSButton" VerticalAlignment="Top" Width="75" Click="ReadRSSButton_Click" />
<sdk:Label Height="29" HorizontalAlignment="Left" Margin="8,12,0,0" Name="label1" VerticalAlignment="Top" Width="69" Content="RSS Adresi" FontSize="10" />
<ListBox Height="180" HorizontalAlignment="Left" Margin="6,105,0,0" Name="RSSListBox" VerticalAlignment="Top" Width="382" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title.Text}" Foreground="BlueViolet" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Height="23" HorizontalAlignment="Left" Margin="8,47,0,0" Name="RSSTextBox" VerticalAlignment="Top" Width="380" />
<sdk:Label Height="40" HorizontalAlignment="Left" Margin="8,297,0,0" Name="RSSInfoLabel" VerticalAlignment="Top" Width="380" FontSize="9" />
</Grid>
</UserControl></pre>
<p>Aslında teori son derece basitti. Kullanıcı <strong>TextBox </strong>kontrolü üzerinden bir <strong>RSS </strong>adresi girecekti. Sonra düğmeye basarak içeriğin <strong>ListBox </strong>kontrolüne dolmasını seyredecekti. Son derece basit ve masumane bir talep öyle değil mi? <img title="Undecided" src="/editors/tiny_mce3/plugins/emotions/img/smiley-undecided.gif" alt="Undecided" border="0" /> Tabi bu işlemler için kod tarafını da, heyecanlı bir şekilde aşağıdaki gibi geliştirmeye çalıştım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Net;
using System.ServiceModel.Syndication;
using System.Windows;
using System.Windows.Controls;
using System.Xml;
namespace RSSReaderim
{
public partial class MainPage : UserControl
{
WebClient client;
public MainPage()
{
InitializeComponent();
// WebClient nesnesi örneklenir
client = new WebClient();
// RSS Adresinden okuma işlemi tamamlanınca devreye girecek olan olay metodu yüklenir
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
}
void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null) // Eğer okuma işlemi sırasında bir hata oluşmadıysa
{
// RSS bilgisi e.Result üzerinden Stream şeklinde elde edilir ve XmlReader nesnesinin örneklenmesi için kullanılır. Bu gereklidir nitekim SyndicationFeed.Load metodu XmlReader tipi ile çalışmaktadır.
XmlReader xReader = XmlReader.Create(e.Result);
// System.ServiceModel.Syndication.dll Assembly' ının projeye referans edilmesi gerekmektedir.
SyndicationFeed feed = SyndicationFeed.Load(xReader);
// Items koleksiyonu ListBox bileşenine veri kaynağı olarak bağlanır
RSSListBox.ItemsSource = feed.Items;
}
else if(e.Error!=null)
{
// Bir hata oluştuysa istisna mesajını Label kontrolünde göster
RSSInfoLabel.Content = String.Format("Bir Sorun oluştu. {0}", e.Error);
}
}
private void ReadRSSButton_Click(object sender, RoutedEventArgs e)
{
// Okuma işlemini başlat.
// Örnek RSS Adresi : http://www.buraksenyurt.com/syndication.axd?format=rss
if (!String.IsNullOrEmpty(RSSTextBox.Text))
client.OpenReadAsync(new Uri(RSSTextBox.Text));
else
RSSInfoLabel.Content = "Lütfen bir RSS Adresi giriniz";
}
}
}</pre>
<p>Kod parçasından da görüldüğü üzere <strong>WebClient </strong>tipini kullanarak <strong>TextBox </strong>kontrolüne girilen adres için bir talepte bulunulmaktadır. Söz konusu talepin sonucu elde edildiğinde devreye giren olay metodu içerisinde ise, öncelikli olarak bir hata kontrolü yapılmaktadır. Eğer herhangibir hata söz konusu değilse <strong>SyndicationFeed </strong>tipinden yararlanılarak elde edilen <strong>Stream</strong> referansının <strong>Feed </strong>olarak ele alınabilmesi amacıyla gerekli işlemler yapılmaktadır. Son olarak söz konusu içerik nesnesi üzerinden ulaşılan <strong>Items </strong>koleksiyonu, <strong>ListBox </strong>kontrolüne bağlanır.</p>
<p>Şimdi <strong>blog </strong>girdimizin başında yer alan resmi açıklayalım. Bu kadar süratli araba kullanırsanız duvara toslamanız an meselesi olabilir. Aynen örneğimizde şu an tosladığımız gibi <img title="Undecided" src="/editors/tiny_mce3/plugins/emotions/img/smiley-undecided.gif" alt="Undecided" border="0" /> İşte duvara tosladığımız anda saniyenin milyonda birinde şişen hava yastığı içinden fırlayan <strong>Exception </strong>mesajımız.</p>
<p><img src="/pics/2010%2f4%2fblg176_Exception.gif" alt="" /></p>
<p>Hayda breeeee!!! <img title="Surprised" src="/editors/tiny_mce3/plugins/emotions/img/smiley-surprised.gif" alt="Surprised" border="0" /> İşte hızlı gitmenin doğal sonucu.</p>
<p>Aslında gözden kaçırdığımız çok önemli bir durum söz konusu. O da <strong>Silverlight </strong>tarafında önem arz eden konuların başında gelen<strong> Cross-Domain Policy</strong> vakası. Sonuç itibariyle <strong>RSS </strong>çıktısı için talepte bulunduğumuz <strong>Domain </strong>adresi ile örneği geliştirmekte olduğumuz <strong>Asp.Net Development Server</strong>' ın <strong>port </strong>numarası eşliğine açtığı <strong>Domain</strong> adresleri birbirlerinden farklı. Bu sebepten sunucu tarafının bir <strong>ClientAccessPolicy.xml </strong>dosyasına sahip olması ve içerisinde söz konusu talepler için gerekli garanti haklarını belirtmiş olması şart. Ancak bu senaryoya göre <strong>Silverlight </strong>istemcileri için <strong>Cross-Domain Policy </strong>desteği vermeyen hiç bir sunucudan <strong>RSS </strong>içeriğini okumamız mümkün değil. Peki öyleyse ne yapacağız? Çözüm olarak biraz dolambaçlı bir yol olsa da, aşağıdaki şekilde görülen planı izleyebiliriz.</p>
<p><img src="/pics/2010%2f4%2fblg176_Plan.gif" alt="" /></p>
<p>Biliyoruz ki, <strong>Silverlight </strong>uygulamaları <strong>Asp.Net</strong> gibi Web uygulamaları içerisinde host edilebilmektedir. Planımıza göre<strong> Cross-Domain Policy</strong> sorunu ile karşılaşmayacak olan <strong>WCF Service</strong>' lerinin, <strong>Silverlight </strong>istemcilerinin talep edeceği <strong>RSS </strong>içeriklerini ele alması söz konusudur. Buna göre <strong>Silverlight </strong>istemcileri, <strong>RSS </strong>çıktılarına doğrudan talepte bulunmak yerine söz konusu taleplerini önce arada <strong>Proxy </strong>görevini üstlenen bir <strong>WCF </strong>servisine iletecektir. Bu <strong>WCF </strong>servisi, ilgili adres bilgisini alarak <strong>Feed </strong>çıktısını talep edecek ve elde ettiği içeriği tekrardan <strong>Silverlight </strong>tarafına gönderecektir.</p>
<p>Şekildeki plana göre <strong>1</strong> ve <strong>2 </strong>numaralı iki adet <strong>WCF </strong>servisi söz konusudur. Bunlardan hangisinin seçileceği tamamen tercihe bağlıdır. İstersek <strong>Silverlight </strong>uygulaması ile aynı <strong>Domain </strong>içerisinde yer alan bir <strong>WCF </strong>servisini, istersek <strong>IIS </strong>üzerinde konuşlandırılan ayrı bir <strong>WCF </strong>servisini kullanabiliriz. Tabi <strong>IIS</strong> üzerinde host edilen bir <strong>WCF </strong>Servisi söz konusu ise, <strong>Silverlight </strong>istemcisi ile olan iletişiminin güvenlik sorununa takılmaması için <strong>ClientAccessPolicy.xml </strong>kullanılması gerekecektir. Tercih tamamen geliştiriciye bağlıdır. Ancak aynı Web sunucusu üzerinde yer alan birden fazla <strong>Silverlight </strong>istemcisi söz konusu ise ilgili servisin IIS altında konuşlandırılması daha çok tercih edilebilir.</p>
<blockquote>
<p>Bu noktada hazır olarak Siverlight istemcilerine hizmette bulunabilen Feed servislerinden de yararlanabileceğimizi belirtmek isterim. <a id="viewpost_ascx_TitleUrl" class="title" title="Title of this entry." href="http://timheuer.com/blog/archive/2008/06/03/use-silverlight-with-any-feed-without-cross-domain-files.aspx">Reading data and RSS with Silverlight and no cross-domain policy</a> başlıklı yazıda Tim Heuer söz konusu servislerden bahsetmektedir.</p>
</blockquote>
<p>Ben örneğimizde hız kesemeden devam edebilmek adına, aynı uygulamaya <strong>Silverlight </strong>destekli bir <strong>WCF </strong>servisini ekleyerek ilerlemeyi tercih ettim. İşte <strong>FeedReaderService </strong>isimli <strong>Silverlight </strong>servisinin kod içeriği.</p>
<blockquote>
<p>Silverlight destekli WCF Servicelerinin nasıl geliştirileceğini <a class="postheader taggedlink" href="https://www.buraksenyurt.com/post/Screencast-Silverlight-Enabled-WCF-Services">Screencast - Silverlight Enabled WCF Services</a> isimli görsel dersten takip edebilirsiniz.</p>
</blockquote>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Syndication;
using System.Web.Services.Protocols;
using System.Xml;
namespace RSSReaderim.Web
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class FeedReaderService
{
[OperationContract]
public List<SyndItem> ReadRss(string address)
{
List<SyndItem> feedItems = null;
try
{
// XmlReader.Create metodu parametre olarak address bilgisini almaktadır. Elde edilen Xml içeriği Load metodu yardımıyla çekilir ve SyndicationItem tipinden olan Items koleksiyonu çekilir.
var syndicationItems =SyndicationFeed.Load(XmlReader.Create(address)).Items;
// SyndicationItem örneklerinin her biri ele alınıp yeni bir SyndItem örneklenmesinde kullanılır.
feedItems = (from syndicationItem in syndicationItems
select new SyndItem
{
Title = syndicationItem.Title.Text,
PublishDate = syndicationItem.PublishDate.DateTime,
Summary = syndicationItem.Summary.Text,
Link=syndicationItem.Links[0].Uri
}
).ToList();
}
catch(Exception excp)
{
throw new SoapException("Bir hata oluştu", new XmlQualifiedName("RssReadError"), excp);
}
return feedItems;
}
}
// SyndicationItem tipi serileştirme sorununa neden olduğundan araya bir Surrogate tip alınmıştır. Bu tip içerisinde Silverlight tarafı için gerekli temel Feed bilgileri yer almaktadır.
public class SyndItem
{
public string Title { get; set; }
public DateTime PublishDate { get; set; }
public string Summary { get; set; }
public Uri Link { get; set; }
//TODO: Diğer bilgilerde getirilmelidir. Örneği yazar bilgisi, son güncellenme tarihi veya kategoriler.
}
}</pre>
<p>Servis kodunda dikkat edilmesi gereken en önemli noktalardan birisi, <strong>ReadRss </strong>metodunun geriye <strong>SyndItem </strong>tipinden generic bir <strong>List </strong>koleksiyonu döndürmesidir. Bu noktada akla şu soru gelebilir. Neden <strong>List<SyndicationItem></strong> gibi bir koleksiyon döndürmüyoruz? <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Aslında buradaki sorun <strong>SyndicationItem </strong>tipinin serileştirme işlemi sırasında çalışma zamanı hatasına neden olmasıdır. Serileştirmedeki bu sıkıntı bizi alternatif bir yola itmiştir. Bu sebepten örnekte bir <strong>Surrogate </strong>tip kullanılmaktadır. Bu işlemin ardından artık <strong>Silverlight </strong>tarafı için gerekli geliştirmeler yapılabilir. İlk etapta aynı <strong>Domain </strong>içerisindeki<em>(bir başka deyişle aynı Solution içerisindeki) </em><strong>WCF Servisinin Silverlight </strong>projesine eklenmesi gerekmektedir.</p>
<p><img src="/pics/2010%2f4%2fblg176_AddServiceRef.gif" alt="" /></p>
<p>Sonrasında ise istemci tarafı için gerekli kodlar yazılabilir. Yeni örnekte <strong>XAML </strong>içeriği de aşağıdaki gibi düzenlenmiştir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><UserControl x:Class="RSSReaderim.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="337" d:DesignWidth="394" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="RSS Oku" Height="23" HorizontalAlignment="Left" Margin="313,76,0,0" Name="ReadRSSButton" VerticalAlignment="Top" Width="75" Click="ReadRSSButton_Click" />
<sdk:Label Height="29" HorizontalAlignment="Left" Margin="8,12,0,0" Name="label1" VerticalAlignment="Top" Width="69" Content="RSS Adresi" FontSize="10" />
<ListBox Height="180" HorizontalAlignment="Left" Margin="6,105,0,0" Name="RSSListBox" VerticalAlignment="Top" Width="382" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="2" Background="Black">
<TextBlock Text="{Binding Title}" Foreground="Gold" />
<TextBlock Text="{Binding Link}" Foreground="LightCyan" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Height="23" HorizontalAlignment="Left" Margin="8,47,0,0" Name="RSSTextBox" VerticalAlignment="Top" Width="380" />
<sdk:Label Height="40" HorizontalAlignment="Left" Margin="8,297,0,0" Name="RSSInfoLabel" VerticalAlignment="Top" Width="380" FontSize="9" />
</Grid>
</UserControl></pre>
<p>Bu kez <strong>ListBox.ItemTemplate </strong>içerisinde hem <strong>Title </strong>hemde <strong>Link </strong>bilgilerinin gösterilmesi sağlanmıştır. Yeni örnekte <strong>SnydItem </strong>isimli bir <strong>Surrogate </strong>tip söz konusu olduğundan ve bu tipin <strong>Title </strong>özelliği <strong>String </strong>tipten tanımlandığından, bir önceki <strong>XAML</strong> kodunda yer alan <strong>{Binding Title.Text} </strong>eşitlemesi <span style="text-decoration: underline;">kullanılmamalıdır</span>. Gelelim kodlarımıza;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Windows;
using System.Windows.Controls;
using RSSReaderim.FeedReaderServiceSpace;
namespace RSSReaderim
{
public partial class MainPage : UserControl
{
FeedReaderServiceClient client = null;
public MainPage()
{
InitializeComponent();
client = new FeedReaderServiceClient();
client.ReadRssCompleted += new EventHandler<ReadRssCompletedEventArgs>(client_ReadRssCompleted);
}
private void ReadRSSButton_Click(object sender, RoutedEventArgs e)
{
client.ReadRssAsync(RSSTextBox.Text);
}
void client_ReadRssCompleted(object sender, ReadRssCompletedEventArgs e)
{
if (e.Error != null)
{
RSSInfoLabel.Content = e.Error;
}
else if (e.Cancelled)
{
RSSInfoLabel.Content = "İşlem iptal edildi";
}
else
{
RSSListBox.ItemsSource = e.Result;
}
}
}
}</pre>
<p>Kod içeriğinden de görüldüğü üzere <strong>WCF </strong>servisine ait <strong>Proxy </strong>tipinden yararlanılarak <strong>Feed </strong>içeriğinin asenkron olarak ortama çekilmesi işlemi gerçekleştirilmektedir. İşte örnek çalışma zamanı çıktılarından birisi.</p>
<p><img src="/pics/2010%2f4%2fblg176_Runtime.gif" alt="" /></p>
<p>ve diğer bir örnek;</p>
<p><img src="/pics/2010%2f4%2fblg176_Runtime2.gif" alt="" /></p>
<p>Görüldüğü üzere <strong>RSS </strong>içerikleri başarılı bir şekilde getirilebilmektedir. Elbetteki örnekte eksik olan bir çok kısım vardır. Söz gelimi <strong>RSS </strong>ile ilişkili olarak daha çok verinin getirilmesi daha iyi olacaktır. Söz gelimi <strong>Feed</strong>' in sahibi olan siteye ait bilgiler. Diğer yandan eksik kalan önemli noktalardan biriside <strong>ListBox</strong>' ta bir öğe seçildiğinde ilgili <strong>Feed </strong>adresine nasıl gidileceğidir. Sonuç itibariyle Silverlight uygulaması tarayıcı üzerinde çalışmaktadır ve ilgili <strong>Feed </strong>içeriğinin <strong>Content </strong>verisinin gösterilmesini herkes isteyecektir. İşte size güzel bir araştırma konusu ve ödev <img title="Smile" src="/editors/tiny_mce3/plugins/emotions/img/smiley-smile.gif" alt="Smile" border="0" /> Benden buraya kadar. Bir süre dinlenmeye çalışacağım. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f4%2fRSSReaderim_RTM.rar">RSSReaderim_RTM.rar (1,48 mb)</a><strong>[Örnek Visual Studio 2010 Ultimate RTM sürümünde geliştirilmiş ve test edilmiştir]</strong></p>2010-07-22T09:05:00+00:00silverlightwcfsyndicationwcf eco systembsenyurtBiliyoruz ki, Silverlight uygulamaları Asp.Net gibi Web uygulamaları içerisinde host edilebilmektedir. Planımıza göre Cross-Domain Policy sorunu ile karşılaşmayacak olan WCF Service' lerinin, Silverlight istemcilerinin talep edeceği RSS içeriklerini ele alması söz konusudur.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=70a0a3b3-b72a-41b8-afc9-9292e10569240https://www.buraksenyurt.com/trackback.axd?id=70a0a3b3-b72a-41b8-afc9-9292e1056924https://www.buraksenyurt.com/post/Silverlight-Tarafindan-Feed-Okumak#commenthttps://www.buraksenyurt.com/syndication.axd?post=70a0a3b3-b72a-41b8-afc9-9292e1056924https://www.buraksenyurt.com/post/Silverlight-Tarafinda-HTTP-Bazli-Servisleri-KullanmakSilverlight Tarafında HTTP Bazli Servisleri Kullanmak2010-07-12T00:55:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f4%2fblg175_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Eğitmenlik yaptığım yıllarda <strong>Microsoft</strong>' un ders kitaplarında yer alan <strong>LAB </strong>çalışmalarını mümkün mertebe yapmaya ve yaptırmaya çalışırdım. Hatta çoğu zaman eğitimlere hazırlanırken sık sık bu lab çalışmalarını kendim yapar ve hatta ek ilaveler ile daha da eğlenceli hale getirmeye çalışırdım. Tabi bazen elimizde lab yapacağımız kitaplarımız olmazdı ki o ayrı bir hikaye.</p>
<p>Lab çalışmaları öğrencinin adım adım yapması gerekenleri söylerek, konunun en yalın haliyle anlaşılmasını sağlamakta önemli rol oynamaktadır. Lab çalışmalarındakine benzer konu anlatımları benimde özümsediğim ve faydalı bulduğum öğrenme tekniklerinden birisidir. İşte bu yazımızda da bu kültüre uymaya çalışarak ilerlemeye çalışıyor olacağız. Hedefimiz <strong>Silverlight </strong>uygulamalarından, <strong>HTTP </strong>tabanlı taleplere göre operasyonel hizmetlerde bulunan servisleri nasıl kullanabileceğimizi, en yalın haliyle görmek. Haydi o zaman lab için gerekli materyalleri değerlendirerek yola koyulalım.</p>
<p><strong>Adım 0 : Mevzumuz</strong></p>
<p>Bilindiği üzere bazı servisler <strong>HTTP </strong>protokolü üzerinden <strong>GET, POST, PUT</strong> veya <strong>DELETE </strong>metod çağrıları ile kullanılabilmektedir. Bu anlamda <strong>WCF Eco System</strong> içerisinde yer alan <a title="WCF WebHttp Services" href="https://www.buraksenyurt.com/archive.aspx#WCF-WebHttp-Services" target="_blank">WebHTTP servisleri</a>, söz konusu tipteki hizmetleri sunmak üzere WCF alt yapısı üzerine oturmuş bir model sunmaktadır. Çok doğal olarak <strong>Silverlight </strong>tabanlı istemciler de bu servislerin tüketicileri olabilirler. Bu tip servislerin kullanıldığı senaryolarda istemci tarafında herhangibir <strong>Proxy </strong>tipi söz konusu olmadığı için, <strong>HTTP GET,POST,PUT </strong>veya <strong>DELETE </strong>metodlarının manuel olarak hazırlanması ve gönderilmesi gerekmektedir. <strong>Silverlight </strong>tarafında bu işlemler için <strong>WebClient </strong>veya <strong>HttpWebRequest </strong>tiplerinden yararlanılabilmektedir. Biz bu yazımızda <strong>WebClient </strong>tipinden yararlanarak, <strong>IIS(Internet Information Services)</strong> üzerinde konuşlandırılmış basit bir <strong>WebHttp Service</strong> örneğinin nasıl kullanılabileceğini incelemeye çalışıyor olacağız.</p>
<p><strong>Adım 1 : WCF Rest Application Uygulaması ve Entity Data Model' in Oluşturulması<br /></strong></p>
<p>İşe ilk olarak <strong>WCF Rest Service Application</strong> şablonunda bir proje oluşturarak başlayabiliriz. Bildiğiniz üzere bu proje şablonu<strong>(Project Template)</strong> hali hazırda yüklü değilse <strong>Online Template</strong>' ler arasından <strong>install </strong>etmeniz gerekmektedir. Söz konusu örnekte <strong>Chinook </strong>veritabanında yer alan ve çok basit olarak ilerlemek istediğimizden sadece <strong>Album </strong>tablosunu içeren bir <strong>Entity Data Model </strong>kullanabiliriz. Aşağıdaki şekilde örneğimizde kullanmakta olduğumuz Entity Data Model yer almaktadır.</p>
<p><img src="/pics/2010%2f4%2fblg175_Edm.gif" alt="" /></p>
<p> </p>
<p><strong>Adım 2 : WebHttp Service Örneğinin Geliştirilmesi</strong></p>
<p><strong>Entities </strong>isimli <strong>WCF WebHttp Service</strong> sınıfımızın içeriğini ise aşağıdaki gibi düzenlediğimizi düşünebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
namespace ChinookDataPortal
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Entities
{
[WebGet(UriTemplate = "Albums/All")]
public List<Album> GetAlbums()
{
List<Album> albums = null;
ChinookEntities entities = new ChinookEntities();
albums=(from albm in entities.Albums
orderby albm.Title
select albm).ToList();
return albums;
}
[WebGet(UriTemplate="Albums/{firstLetter}")]
public List<Album> GetAlbumsByFirstLetter(string firstLetter)
{
List<Album> albums = null;
ChinookEntities entities = new ChinookEntities();
albums = (from albm in entities.Albums
where albm.Title.ToLower().StartsWith(firstLetter.ToLower())
orderby albm.Title
select albm).ToList();
return albums;
}
}
}</pre>
<p>Servis tipimiz iki operasyon içermekte olup her ikiside <strong>HTTP Get </strong>çağrılarına cevap verecek şekilde düzenlenmişlerdir. <strong>GetAlbums</strong> metoduna yapılan çağrılarda servis <strong>URL </strong>adresine <strong>Albums/All </strong>takısı eklenmelidir. Diğer yandan ilk harflerine göre albümleri listeleyen <strong>GetAlbumsByFirstLetter </strong>metodu, <strong>URL </strong>adresine <strong>Albums/{firstLetter} </strong>bilgisinin eklenmesini beklemektedir. Her iki metod <strong>ChinookEntities </strong>tipini kullanmakta ve basit <strong>LINQ </strong>sorguları ile sonuç üretmektedir. Servisimizi bu şekilde geliştirdikten sonra IIS altına Publish ederek devam edebiliriz.</p>
<p><strong>Adım 3 : IIS Publish</strong></p>
<p>Publish işlemleri için aşağıdaki şekilde görülen Profile ayarlarını kullanabilirsiniz.</p>
<p><img src="/pics/2010%2f4%2fblg175_PublishProfile.gif" alt="" /></p>
<p>Bu ayarlara göre servisimizin <strong>IIS </strong>üzerinde yer alan <strong>Default Web Site</strong> isimli <strong>Application Pool </strong>altına dağıtılacağı belirtilmiş olunur.</p>
<p><em><strong>Not: IIS </strong>üzerinden <strong>Convert To Application </strong>işlemini yapmanız gerekebilir.</em></p>
<p>Sonuç olarak <strong>IIS </strong>içerisinde aşağıdaki gibi servisin üretilmiş olması gerekmektedir. Bu arada örneği geliştirdiğimiz makinede <strong>Windows 7 Enterprise </strong>işletim sisteminin ve <strong>IIS 7.5.7600.16385 </strong>sürümünün olduğunu belirtelim.</p>
<p><img src="/pics/2010%2f4%2fblg175_IIS.gif" alt="" /></p>
<p>Bu noktadan sonra Silverlight uygulamasının geliştirilmesi aşamına geçilecektir. Ancak öncelikle gerekli testleri yapılmasında yarar vardır.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f4%2fChinookDataPortal.rar">ChinookDataPortal.rar (46,54 kb)</a><strong> [Örnek Visual Studio 2010 Ultimate RC ortamında geliştirilmiş ve test edilmiştir]</strong></p>
<p><strong>Adım 4 : WebHttp Service Test<br /></strong></p>
<p><strong>Silverlight </strong>tarafındaki uygulamamızı geliştirmeden önce servisimizi <strong>IIS </strong>üzerinden test etmemizde ve çalıştığından emin olmamızda yarar olacağı kanısındayım. İlk olarak yardım sayfasına ulaşıp ulaşamadığımızı öğrenelim. Bilindiği üzere WebHttp Service örnekleri aksi belirtilmedikçe hazır bir yardım sayfası sunmaktadır. Bu amaçla tarayıcı uygulamadan <strong>http://localhost/ChinookDataPortal/Entities/help </strong>şeklinde bir talepte bulunduğumuzda, aşağıdaki ekran çıktısı ile karşılaşmış olmalıyız.</p>
<p><img src="/pics/2010%2f4%2fblg175_HelpPage.gif" alt="" /></p>
<p>Yardım sayfasının çalışıyor olması dışında servis tarafında yer alan operasyonel metodların da test edilmesinde yarar vardır. Örneğin tüm albümleri elde etmek için<strong> http://localhost/ChinookDataPortal/Entities/Albums/All </strong>şeklinde talepte bulunduğumuzda, aşağıdaki ekran görüntüsünde yer alan sonuçları elde etmiş olmamız gerekmektedir. Tabi veri içeriklerinde değişiklikler söz konusu olabilir. Ancak XML çıktısının şematik yapısının benzer olması gerekmektedir.</p>
<p><img src="/pics/2010%2f4%2fblg175_AllAlbums.gif" alt="" /></p>
<p>Son olarak örneğin <strong>Cake </strong>adı ile başlayan albümleri çekmek istediğimizi ve bu amaçla <strong>URL </strong>satırından <strong>http://localhost/ChinookDataPortal/Entities/Albums/Cake </strong>şeklinde bir talep gönderdiğimizi düşünelim. Bu durumda ekran çıktısının aşağıdakine benzer olması gerekmektedir.</p>
<p><img src="/pics/2010%2f4%2fblg175_ByFirstLetter.gif" alt="" /></p>
<p>Eğer bu sonuçları elde edebiliyorsak servisimizin çalıştığını ve <strong>Sliverlight </strong>tarafı için kullanılabilir olduğunu söyleyebiliriz. Lakin dikkat etmemiz gereken bir nokta daha vardır.</p>
<p><strong>Adım 5 : Client Access Policy Ayarları</strong></p>
<p><strong>Silverlight </strong>uygulamamızın farklı bir <strong>Domain </strong>içerisinde <strong>host </strong>edilmesine karşılık, <strong>IIS </strong>üzerinde gerekli<strong> Client Access Policy</strong> ayarlarının bulunması gerekmektedir. Bu nedenle<strong> IIS root </strong>klasörü altında yer alması gereken <strong>ClientAccessPolicy.xml </strong>dosyasının içeriğini aşağıdaki gibi düzenleyebiliriz.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding ="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from>
<domain uri="*" />
</allow-from>
<grant-to>
<resource path="/WorldWeatherService" include-subpaths="true"/>
<resource path="/ChinookDataPortal" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy></pre>
<p>Burada görüleceği üzere ChinookDataPortal ve alt yollarına erişim izni verilmiştir. Artık <strong>Silverlight </strong>tarafını geliştirmeye başlayabiliriz.</p>
<p><strong>Adım 6: Silverlight Application Projesinin Oluşturulması</strong></p>
<p>Bu amaçla <strong>Visual Studio 2010</strong> ortamında <strong>ConsumingHTTPBasedServices </strong>isimi ve <strong>Silverlight 4.0</strong> tabanlı bir <strong>Application </strong>oluşturduğumuzu düşünelim. Söz konusu uygulamada RIA Service kullanılmayacağı için bu seçeneği pasif olarak bırakabiliriz. Bu işlem sonucu oluşturulan <strong>MainPage </strong>sayfasına ait <strong>XAML </strong>içeriğini ise aşağıdaki gibi geliştirebiliriz.</p>
<p><strong>Adım 7 : MainPage.Xaml içeriği ve Kodun Yazılması</strong></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><UserControl x:Class="ConsumingHTTPBasedServices.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="517">
<Grid x:Name="LayoutRoot" Background="White">
<ListBox Height="203" HorizontalAlignment="Left" Margin="8,70,0,0" Name="AlbumListBox" VerticalAlignment="Top" Width="497" />
<StackPanel Height="54" HorizontalAlignment="Left" Margin="9,10,0,0" Name="ButtonsStackPanel" VerticalAlignment="Top" Width="496" Orientation="Horizontal" />
</Grid>
</UserControl></pre>
<p><strong>MainPage </strong>içerisinde yer alan <strong>ListBox </strong>kontrolü içeriği<strong> A' dan Z' ye harfler</strong> ile doldurulacaktır. Herhangibir harfe basıldığında, <strong>WebHttp Service</strong>' imiz için bir <strong>HTTP Get </strong>talebi oluşturulacak ve sonuçların <strong>ListBox </strong>içerisinde gösterilmesi sağlanacaktır. Bu amaçla kod içeriğini aşağıdaki gibi geliştirmemiz yeterlidir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Linq;
using System.Net;
using System.Windows.Controls;
using System.Xml;
using System.Xml.Linq;
namespace ConsumingHTTPBasedServices
{
public partial class MainPage
: UserControl
{
// WebHttp Servisine basit HTTP metodları ile talepte bulunabilmemizi sağlayan WebClient nesnesi tanımlanır
WebClient client;
public MainPage()
{
InitializeComponent();
// WebClient nesnesi örneklenir
client=new WebClient();
// Belirtilen URL adresine yapılan talep sonucu gerçekleşecek okuma işlemi tamamlandığında(bir başka deyişle veri istemci tarafında indirildiğinde) devreye girecek olan olay metodu tanımlanır.
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
// A...Z Button üretimleri gerçekleştirilir
for (int i = 65; i < 91; i++)
{
Button btn = new Button();
btn.Width = 18;
btn.Height = 18;
btn.FontSize = 10;
btn.Content = ((char)i).ToString();
ButtonsStackPanel.Children.Add(btn);
// Herhangibir Button tıklandığında
btn.Click += (o, e) =>
{
// Önce WebHttp Service' ne doğur yapılacak HTTP Get talebi için gerekli URI oluşturulur
Uri address = new Uri(String.Format("http://localhost/ChinookDataPortal/Entities/Albums/{0}", ((Button)o).Content));
// Belirtilen URI talebi asenkron olarak çalışan OpenReadAsycn metodu ile gönderilir
client.OpenReadAsync(address);
};
}
}
// URI ile belirtilen adres talebi gerçekleştirilip ilgili veri içeriği istemci tarafına indirildikten sonra devreye giren olay metodudur
void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
// İçerik bir Stream olarak gelmektedir ve tasarlanan ChinookDataPortal WebHttp Servisi varsayılan olarak XML içerik göndermektedir.
// Bu sebepten Stream XmlReader ile okunur
XmlReader xReader = XmlReader.Create(e.Result);
// XLINQ sorgusunun yapılabilmesi için XElement.Load metodu parametre olarak Stream' i kullanan XmlReader nesne örneğini alır
XElement xElement = XElement.Load(xReader);
// XLINQ sorgusu ile Title elementleri çekilir. XName.Get metodunun ikinci parametre XML Namespace' inin adıdır.
var titles = from x in xElement.Elements().Elements(XName.Get("Title", "http://schemas.datacontract.org/2004/07/ChinookDataPortal"))
select x.Value;
// Çekilen veri içeriği ListBox kontrolünün ItemsSource özelliğine bağlanır
AlbumListBox.ItemsSource = titles;
}
}
}</pre>
<blockquote>
<p>XElement tiplerini kullanabilmek ve XLINQ sorgularını yazabilmek için, Silverlight uygulamasına(ConsumingHTTPBasedServices.Web uygulamasına değil) System.Xml.Linq.dll Assembly' ının referans edilmesi gerekmektedir.</p>
</blockquote>
<p><strong>Adım 8 : Silverlight Uygulamasının Test Edilmesi</strong></p>
<p>Dilerseniz uygulamanın çalışma zamanı sonuçlarına hemen bakalım. Böylece çalışma zamanı testlerini yapmış oluruz. Örneğin <strong>A</strong> başlıklı <strong>Button </strong>kontrolüne bastığımızda, aşağıdaki ekran görüntüsündekine benzer sonuçları almış olmalıyız. Yani Title alanındakilerden A harfi ile başlayanların listesinin elde edilebiliyor olması gerekmektedir.</p>
<p><img src="/pics/2010%2f4%2fblg175_Runtime1.gif" alt="" /></p>
<p>Görüldüğü üzere <strong>ListBox </strong>içeriği baş harfi A olan albüm adları ile doldurulmuştur. Hemen bu işlemin arkasından örneğin <strong>C</strong> başlıklı <strong>Button </strong>kontrolüne basarsak aşağıdaki sonuçlar ile karşılaştığımız görürüz.</p>
<p><img src="/pics/2010%2f4%2fblg175_Runtime2.gif" alt="" /></p>
<p>Süper değil mi? <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /></p>
<p><strong>Özet</strong></p>
<p>Tabi bu örnekte dikkat edilmesi gereken noktalardan birisi de, istemci tarafında herhangibir <strong>Proxy </strong>tipinin olmayışıdır. Bunun yerine <strong>HTTP Get</strong> metodu ile talepte bulunulmuş ve elde edilen <strong>Stream </strong>üzerindeki <strong>XML </strong>içeriği değerlendirilmiştir. Diğer yandan çok doğal olarak <strong>Servis </strong>tarafında kullanılan <strong>Entity Data Model</strong> içerisindeki tiplerin istemci tarafındaki karşılıkları <span style="text-decoration: underline;">bulunmamaktadır</span>. Eğer bu tiplerin istemci tarafında ele alınması arzu edilirse açık bir şekilde oluşturulmaları gerekecektir. Tabi böyle bir senaryoda gelen <strong>XML </strong>veya <strong>JSON </strong>tipindeki içeriğinde ilgili tiplere dönüştürülmesi gibi bir işlem söz konusu olacaktır.</p>
<p><strong>Ödev</strong> <img title="Smile" src="/editors/tiny_mce3/plugins/emotions/img/smiley-smile.gif" alt="Smile" border="0" /></p>
<ol>
<li>Servisin <strong>XML </strong>yerine <strong>JSON(JavaScript Object Notation)</strong> formatında bir çıktı vermesi halinde, <strong>Silverlight </strong>tarafında gerekli olan kod düzenlemelerini yapınız.</li>
<li>Servis üzerinden <strong>HTTP Put </strong>metod ile güncelleme işlemi yapabilmenizi sağlayacak bir geliştirmeyi aynı örnek üzerinden yapmaya çalışınız.</li>
</ol>
<p>Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f4%2fConsumingHTTPBasedServices_RTM.rar">ConsumingHTTPBasedServices_RTM.rar (273,75 kb)</a><strong>[Örnek Visual Studio 2010 Ultimate RC Sürümü üzerinde geliştirişmiş ve RTM sürümü üzerinde test edilmiştir]</strong></p>2010-07-12T00:55:00+00:00wcf webhttp servicessilverlight 4.0silverlightwcfhttpgetpostputdeletewcf eco systembsenyurtBilindiği üzere bazı servisler HTTP protokolü üzerinden GET, POST, PUT veya DELETE metod çağrıları ile kullanılabilmektedir. Bu anlamda WCF Eco System içerisinde yer alan WebHTTP servisleri, söz konusu tipteki hizmetleri sunmak üzere WCF alt yapısı üzerine oturmuş bir model sunmaktadır. Çok doğal olarak Silverlight tabanlı istemciler de bu servislerin tüketicileri olabilirler.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=3cbb8ca2-248d-4823-b7b4-d37b2bca42040https://www.buraksenyurt.com/trackback.axd?id=3cbb8ca2-248d-4823-b7b4-d37b2bca4204https://www.buraksenyurt.com/post/Silverlight-Tarafinda-HTTP-Bazli-Servisleri-Kullanmak#commenthttps://www.buraksenyurt.com/syndication.axd?post=3cbb8ca2-248d-4823-b7b4-d37b2bca4204https://www.buraksenyurt.com/post/WCF-Web-Http-Services-ETagsWCF Web Http Services - ETags2010-04-09T05:05:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f2%2fblg159_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p><strong>WCF WebHttp Service</strong>' leri ile ilişkili yazılarımıza kaldığımız yerden devam ediyoruz. Bu yazımızda <strong>ETag(Entity Tag)</strong> kullanarak sunucu ile istemci arasındaki veri trafiğini nasıl azaltabileceğimizi incelemeye çalışacağız. Öncelikle istemci ile servis arasındaki iletişimi düşünerek ilerlemeye çalışalım. İstemci, sunucu üzerinde yer alan bir operasyon için talepte bulunduğunda bir cevap üretilecek ve buna bağlı bir içerik verisi istemci tarafına indirilecektir.</p>
<p>Bu süreç tipik olarak<strong> Request-Response</strong> senaryosundan farklı bir işleyiş değildir. İstemci sonraki bir zaman diliminde aynı operasyona yeni bir talepte bulunduğunda ise, üretilecek olan sunucu <strong>cevabının(Response)</strong> bir öncekine göre hiç değişmemiş olma ihtimalide bulunmaktadır. Eğer istemci tarafı bir şekilde gönderdiği talebin karşılığı olan cevabın değişmediğini anlayabilirse ve kendisinde bu içerik zaten tampon alanda duruyorsa, aynı içeriğin sunucudan istemci tarafına bir kere daha indirilmesine gerek yoktur. İşte <strong>ETag</strong> takısının devreye girdiği nokta burasıdır.</p>
<p>Peki <strong><a title="ETag Nedir?" href="http://en.wikipedia.org/wiki/HTTP_ETag">ETag(Entity Tag)</a> </strong>tam olarak nedir? <strong>Entity Tag</strong>, sunucudan istemci tarafına gönderilen <strong>Response </strong>paketlerinin <strong>Header </strong>kısmında kullanılabilen bir takıdır. Bu takı yardımıyla bilginin değişikliğe uğrayıp uğramadığı kolayca anlaşılabilir. Bu ayrım bize performans açısından bir kazanım sağlayabilir. Öyleki, sunucu aynı <strong>ETag </strong>verisine sahip iki <strong>Response </strong>ürettiğinde, aslında istemcinin talebinin karşılığının bir öncekisi ile aynı olduğu sonucuna varılabilir. Bu noktada istemcinin içeriği tampon bir bölgede tuttuğu düşünüldüğünde, bir önceki ile aynı olan veri içeriğini sunucudan indirmesine gerek kalmayacaktır. Böyle bir durumda sunucun istemci tarafına <strong>HTTP 304 Not Modified </strong>bilgisi göndermesi söz konusudur. Tabi burada <strong>ETag </strong>içerisine yazılacak verinin nasıl üretileceği de önemlidir. Genellikle <strong>Entity </strong>ile alakalı olaraktan son güncelleme zamanı veya <strong>checksum </strong>kullanılabilir. Hatta <strong>SQL </strong>veritabanında kullanılabilen <strong>Timestamp </strong>tipide <strong>ETag </strong>verisi olarak ciddi anlamda düşünülebilir. Sonuç itibariyle <strong>ETag </strong>kavramının <strong>Caching </strong>modeli için çok önemli bir kriter olduğunu söyleyebiliriz.</p>
<p>Şimdi konuyu aşağıdaki içeriğe sahip bir <strong>WebHttp Service</strong> örneği üzerinden değerlendirmeye çalışalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
namespace Lesson2
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ProductService
{
static List<Product> products = new List<Product>
{
new Product{ ProductId="1",Name="Zedojen 500 Gb HDD", Version=Guid.NewGuid()},
new Product{ ProductId="2",Name="Zedojen 750 Gb HDD", Version=Guid.NewGuid()},
new Product{ ProductId="3",Name="Leveno Laptop XK 5301", Version=Guid.NewGuid()}
};
[WebGet(UriTemplate = "Products/{productId}")]
public Product GetProduct(string productId)
{
var product = (from p in products
where p.ProductId == productId
select p).FirstOrDefault();
// Eğer bir Product nesne örneği mevcutsa...
if (product != null)
{
// İstemciden gelen paket Header' ındaki If-None-Match değeri alınır ve sunucu tarafında bulunan Product nesne örneğinin Version özelliğinin değeri ile kıyaslanır. Eğer aynı ise bu istemci tarafına HTTP 304 Not Modified döndürüleceği anlamına gelir.
WebOperationContext.Current.IncomingRequest.CheckConditionalRetrieve(product.Version);
// Response içerisinde yer alan HTTP Header içerisindeki ETag değeri sunucu tarafından bulunan Product nesne örneğinin Version özelliği ile set edilir.
WebOperationContext.Current.OutgoingResponse.SetETag(product.Version);
}
return product;
}
}
public class Product
{
public string ProductId { get; set; }
public string Name { get; set; }
public Guid Version { get; set; }
}
}</pre>
<p>Servisimizde yer alan <strong>GetProduct </strong>isimli operasyon geriye <strong>Product </strong>tipinden bir nesne içeriği döndürmektedir. <strong>Product </strong>tipinin en önemli özelliklerinden birisi ise <strong>Guid </strong>tipinden olan <strong>Version</strong>' dur. Burada veritabanında yer alan bir ürünün <strong>ETag </strong>için kullanılabilecek veri tipi simüle edilmeye çalışılmaktadır. Yazımızın başında da belirttiğimiz gibi tablo bazlı kaynağın söz konusu olması halinde, <strong>Guid </strong>yerine <strong>Timestamp </strong>tipinden bir alan da tercih edilebilir.</p>
<p><strong>GetProduct </strong>metodu içerisinde <strong>Exception </strong>kontrolü yapılmamaktadır. Daha çok üzerinde durmak istediğimiz nokta <strong>ETag </strong>veri kontrolü ve üretimidir. Bu nedenle <strong>WebOperationContext.Current</strong> üzerinden çağırılan <strong>CheckConditionalRetrieve </strong>ve <strong>SetETag </strong>metodlarına konsantre olmamızda yarar vardır. <strong>CheckConditionalRetrieve </strong>metodu istemciden gelen talebe ait içerikteki <strong>If-None-Match </strong>değeri kontrolünü yapmaktadır. Eğer istemci aynı içeriği bir kere daha talep etmişse, bu durumda istediği <strong>Product </strong>tipinin <strong>Version </strong>değeri ile gönderdiği <strong>If-None-Match </strong>değeri aynı olmalıdır. Tabiki buradaki örnek senaryomuzda şu an için veri tarafında yer alan <strong>Version </strong>alanının değişmediğini düşünüyoruz.</p>
<p>Özellikle tarih bazlı olarak tutulan veri içeriklerinde ve örneğin son güncelleme tarihinin <strong>ETag</strong> olarak kullanıldığı durumlarda ya da herhangibir değişiklik sonrası ilgili versiyon kontrolü alanlarının değerlerinin değiştirildiği hallerde, sunucu tarafından istemciye doğru <strong>veri indirilmesi(Download)</strong> işlemi yinelenecektir. <strong>SetETag </strong>metodu ise ilk gelen talep sonrası veya içeriğin istemciye indirilmesi gerektiği talep sonrası, <strong>Response</strong>' a ait içeriğe o anki <strong>Product </strong>nesne örneğinin <strong>Version </strong>değerini atayacaktır. Her iki metodunda farklı tipte<strong> aşırı yüklenmiş(Overload)</strong> versiyonları bulunmaktadır. Bu versiyonlardan birisi de örneğimizde ele aldığımız <strong>Guid </strong>veri tipi ile çalışanıdır. Dilerseniz durumu daha iyi anlamak için hemen testlerimize başlayalım. Örneğimizi tarayıcı uygulama üzerinden talep ettiğimizde ilk etapta aşağıdaki örnek sonuçlar ile karşılaşırız.</p>
<p><img src="/pics/2010%2f2%2fblg159_Runtime.gif" alt="" /></p>
<blockquote>
<p>Burada ipv4.fiddler:1000 şeklindeki kök adres kullanımı mutlaka dikkatinizi çekmiştir. Bunu Fiddler üzerinden örneğimize ait HTTP paketlerini debug edebilmek için kullandığımızı belirtmek isterim.</p>
</blockquote>
<p>Örnekte <strong>ProductId </strong>değeri <strong>1 </strong>olan ürüne ait bilgilerin elde edildiği görülmektedir. Bundan sonra <strong>1 numaralı ürünü tekrardan talep edecek olursak Fiddler </strong>tarafından aşağıdaki <strong>HTTP </strong>hareketliliklerinin yakalandığını görebiliriz.</p>
<p><img src="/pics/2010%2f2%2fblg159_Fiddler1.gif" alt="" /></p>
<p>Dikkatinizi çeken bir şey var mı? <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> İlk talep sonrasında sunucudan istemiye<strong> HTTP 200 Ok</strong> bilgisi dönmüş ve <strong>237 Byte</strong>' lık bir <strong>Body </strong>içeriği indirilmiştir. Diğer yandan aynı talebin ikinci kez yapılması sonrasında istemci tarafına <strong>HTTP 304 Not Modified</strong> mesajının döndürüldüğü görülmektedir. Üstelik ikinci talep sonrası <strong>Body </strong>içeriği <strong>0 byte </strong>uzunluğundadır. Volaaa!!! <img title="Laughing" src="/editors/tiny_mce3/plugins/emotions/img/smiley-laughing.gif" alt="Laughing" border="0" /> Yani ikinci talebin ilki ile aynı veri üretimine sahip olduğu anlaşılmış ve bu sebepten üretilen paketin istemci tarafına yeniden indirilmesine gerek kalınmamıştır. Örneğimize göre <strong>byte </strong>seviyesinde bu çok önemli bir performans kazanımına neden <span style="text-decoration: underline;">olmamaktadır</span>.</p>
<p>Ne varki<strong> video, resim, müzik</strong> gibi büyük boyutlu <strong>binary </strong>içeriklerin yer aldığı paketlerde <strong>304 </strong>döndürülmesinin büyük önemi vardır. Nitekim az önce üretilip istemci tarafına indirilen büyük boyutlu içeriğin, ikinci talep sonrası zaten istemci tarafındaki tamponda duran versiyonu ile aynı olması nedeniyle, yeniden gönderilmesi durumu ortadan kaldırılmakta ve böylece istemci ile sunucu arasındaki ağ trafiğinden akan veri boyutu minimize edilmektedir.</p>
<p>Şimdi <strong>Fiddler </strong>aracı yardımıyla paketlerin içeriklerine biraz daha yakından bakalım. İlk talep sonrası istemcinin gönderdiği içerik aşağıdaki gibidir.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">GET http://127.0.0.1:1000/Adventures/Products/1 HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Accept-Language: tr
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; .NET4.0C; .NET4.0E; MS-RTC LM 8)
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: 127.0.0.1:1000</pre>
<p>Bu talebe karşılık sunucunu cevabı ise aşağıdaki gibi olacaktır.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Fri, 26 Feb 2010 09:43:00 GMT
X-AspNet-Version: 4.0.30128
Content-Length: 237
ETag: "2c1cd636-3058-4985-8f10-3d3cb8c9e5fa"
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Connection: Close
<Product xmlns="http://schemas.datacontract.org/2004/07/Lesson2" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Name>Zedojen 500 Gb HDD</Name><ProductId>1</ProductId><Version>2c1cd636-3058-4985-8f10-3d3cb8c9e5fa</Version></Product></pre>
<p>Dikkat edileceği üzere <strong>Response </strong>içerisinde bir <strong>ETag </strong>değeri olduğu görülmektedir. Sizce bu değerin <strong>1</strong> numaralı <strong>Product</strong>' ın güncel <strong>Version </strong>değeri ile aynı olması bir tesadüf müdür? <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Ayrıca içeriğin uzunluğu <strong>237 byte' </strong>tır.</p>
<p>Gelelim ikinci talebe. İstemci tarafından sunucuya gönderilen ikinci talebin içeriği aşağıdaki gibidir.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">GET http://127.0.0.1:1000/Adventures/Products/1 HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Accept-Language: tr
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; .NET4.0C; .NET4.0E; MS-RTC LM 8)
Accept-Encoding: gzip, deflate
If-None-Match: "2c1cd636-3058-4985-8f10-3d3cb8c9e5fa"
Host: 127.0.0.1:1000
Connection: Keep-Alive</pre>
<p>Dikkat edileceği üzere<strong> Guid </strong>değerine sahip olan<strong> If-None-Match</strong> isimli element bulunmaktadır. Bu değer biraz önceki talep sonucu istemciye gönderilen <strong>ETag </strong>değeridir aslında. Şimdi bu noktada sunucu üzerinde yer alan <strong>1 </strong>numaralı ürünün içeriğinin değiştirilmediği ve bu nedenle <strong>Guid </strong>değerinin de aynı olduğu düşünülmektedir. Bu sebepten sunucu tarafından istemciye gönderilen cevabın içeriği aşağıdaki gibi olacaktır.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">HTTP/1.1 304 Not Modified
Server: ASP.NET Development Server/10.0.0.0
Date: Fri, 26 Feb 2010 09:43:05 GMT
X-AspNet-Version: 4.0.30128
ETag: "2c1cd636-3058-4985-8f10-3d3cb8c9e5fa"
Cache-Control: private
Connection: Close</pre>
<p>Her hangibir<strong> içerik(Content)</strong> yoktur. Hatta<strong> 0 byte</strong> uzunluğunda bir <strong>Content </strong>mevcuttur. Ama daha önemlisi yine <strong>ETag </strong>elementi vardır ve <strong>Guid </strong>değerini içermektedir. Ayrıca <strong>HTTP 304 Not Modified</strong> bilgisinin döndürüldüğü dikkatlerden kaçmamalıdır.</p>
<p>Peki sunucu tarafındaki <strong>Product </strong>içeriğinde ve dolayısıyla <strong>Version </strong>değerinde bir değişme olursa? Bir veritabanı örneği geliştirmediğimiz için bu durumu şu şekilde simüle edebiliriz<br />; <strong>1 </strong>numaralı <strong>ProductId </strong>değerine sahip ürünün adını <strong>Visual Studio 2010 </strong>ortamında değiştirip örneği tekrardan <strong>build </strong>ederek. Yeniden build işlemi sonucu <strong>static </strong>olarak tanımlanan <strong>List<Product> </strong>koleksiyon içeriğinin üretimi <span style="text-decoration: underline;">yinelenecektir</span>. Bu da yeni <strong>Guid </strong>değerlerinin üretimi anlamına gelmektedir. Bu durumda servis operasyonuna yeniden talepte bulunursak aşağıdaki cevabı aldığımız görürüz.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Fri, 26 Feb 2010 11:52:38 GMT
X-AspNet-Version: 4.0.30128
Content-Length: 237
ETag: "2fadf1be-d5c3-4fe0-a9c6-ecf20437ffe4"
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Connection: Close
<Product xmlns="http://schemas.datacontract.org/2004/07/Lesson2" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Name>Zedojen 250 Gb HDD</Name><ProductId>1</ProductId><Version>2fadf1be-d5c3-4fe0-a9c6-ecf20437ffe4</Version></Product></pre>
<p>Dikkat edileceği üzere <strong>Guid </strong>değeri bir öncekinden farklıdır ve istemciye<strong> HTTP 200 Ok</strong> koduyla <strong>Product </strong>içeriği tekrardan gönderilmiştir. Tabi bunun sonrasında 1 numaralı ürünü yeninden talep edersek yine HTTP 304 Not Modified durumu ile karşılaşırız.</p>
<p>Buraya kadar her şey iyi gitti. Ancak testlerimizi farkettiğiniz üzere <strong>Internet Explorer </strong>gibi tarayıcı uygulamalar üzerinden gerçekleştirdik. Oysaki istemci uygulamayı biz yazıyorsa <strong>ETag </strong>kullanımı için de yapmamız gereken ekstra işlemler söz konusudur. Bu amaçla az önce geliştirdiğimiz servis uygulamasını test edeceğimiz basit bir <strong>Console Application</strong> geliştirdiğimizi düşünelim. Kod içeriğini aşağıdaki gibi yazmamız <strong>ETag </strong>desteği için yeterli olacaktır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using Microsoft.Http;
using Microsoft.Http.Headers;
namespace Client
{
class Program
{
static void Main(string[] args)
{
Uri serviceAddress = new Uri(@"http://ipv4.fiddler:1000/Adventures/");
using (HttpClient client = new HttpClient(serviceAddress))
{
EntityTag eTag=null;
Process(client, "1",ref eTag);
Process(client, "1",ref eTag);
}
}
static void Process(HttpClient client, string ProductId,ref EntityTag ETag)
{
Console.WriteLine("***{0} için Talep***\n",ProductId);
// Talebin hazırlanması ve gönderilmesi
using (HttpRequestMessage request = new HttpRequestMessage("GET", "Products/"+ProductId))
{
// Metoda referans olarak gelen EntityTag tipinden olan ETag değerine bakılır. Eğer null değil ise ki ilk talep sonrası sunucu tarafından ürünün Version değeri ile doldurulacaktır; bu durumda Header kısmına If-None-Match değerinin eklenmesi sağlanır.
if (ETag != null)
request.Headers.IfNoneMatch.Add(ETag);
// If-None-Match değeri içeren talep gönderilir
using (HttpResponseMessage response = client.Send(request))
{
// ETag değeri gelen cevaptan alınır ve ref tipinden olan metod parametresine aktarılır. Böylece Process metoduna yapılacak olan sonraki çağrılarda aynı ETag değerinin taşınması kolaylaşmaktadır.
ETag = response.Headers.ETag;
// Sonuçlar ekran yazdırılır.
Console.WriteLine("StatusCode : {0}\n", response.StatusCode);
Console.WriteLine("Content : {0}\n",response.Content.ReadAsString());
Console.WriteLine("ETag Değeri : {0}\n", ETag.Tag);
}
}
Console.WriteLine("*******");
}
}
}</pre>
<p>Uygulamamızı çalıştırdığımızd aşağıdaki sonuçlar ile karşılaşırız.</p>
<p><img src="/pics/2010%2f2%2fblg159_Runtime2.gif" alt="" /></p>
<p>Görüldüğü gibi, ikinci talep sonrasında istemci tarafına <strong>HTTP 304 Not Modified </strong>bilgisi ve <strong>0 Byte </strong>uzunluğunda içerik gönderilmiştir. Böylece<strong> WCF WebHttp Service</strong>' leri ile ilişkili bir yazımızın daha sonuna geldik. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f2%2fLesson9_RC.rar">Lesson9_RC.rar (175,04 kb)</a><strong> [Örnek Visual Studio 2010 Ultimate RC sürümü üzerinde geliştirilmiş ve test edilmiştir]<br /></strong></p>2010-04-09T05:05:00+00:00wcf 4.0wcfwcf webhttp servicesrestbsenyurtWCF WebHttp Service' leri ile ilişkili yazılarımıza kaldığımız yerden devam ediyoruz. Bu yazımızda ETag(Entity Tag) kullanarak sunucu ile istemci arasındaki veri trafiğini nasıl azaltabileceğimizi incelemeye çalışacağız. Öncelikle istemci ile servis arasındaki iletişimi düşünerek ilerlemeye çalışalım. İstemci, sunucu üzerinde yer alan bir operasyon için talepte bulunduğunda bir cevap üretilecek ve buna bağlı bir içerik verisi istemci tarafına indirilecektir.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=05b202f7-c32a-4359-8c3f-22e9ea1578c92https://www.buraksenyurt.com/trackback.axd?id=05b202f7-c32a-4359-8c3f-22e9ea1578c9https://www.buraksenyurt.com/post/WCF-Web-Http-Services-ETags#commenthttps://www.buraksenyurt.com/syndication.axd?post=05b202f7-c32a-4359-8c3f-22e9ea1578c9https://www.buraksenyurt.com/post/WCF-WebHttp-Services-Using-Custom-MessageWCF WebHttp Services - Özel Formatta Mesaj Döndürmek2010-04-08T07:39:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f2%2fblg146_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Bu yazımızda bizleri uzun, zorlu ve yorucu bir macera bekliyor. Şimdiden söylemek isterim ki yanınızda tatlı<em>(Mesela kolalı jelibon olabilir)</em>, tuzlu yiyecek bir şeyler, boğaz kuruluğunuzu giderecek içecekler veya daha fazla oksijen çekmenizi sağlayacak sakızlar olsun. Unutmadan birde aspirin. Baş ağrısı için <img title="Laughing" src="/editors/tiny_mce3/plugins/emotions/img/smiley-laughing.gif" alt="Laughing" border="0" /> Gelelim bu günkü konumuza.</p>
<p>Bu yazımızda, son günlerde sıklıkla üzerinde durduğumuz <strong>WCF WebHttp Service'</strong> lerinde, istemciden gelen root adres bazlı taleplerin nasıl karşılanacağını ve özel formatta mesajların nasıl döndürüleceğini incelemeye çalışıyor olacağız. Ancak işe başlamadan önce ihtiyacın ne olduğundan bahsetmemizde yarar var. Bu amaçla bir <strong>Web</strong> uygulaması üzerinden <strong>host </strong>edilen birden fazla <strong>WebHttp</strong> servisimiz olduğunu düşünerek ilerleyelim. Bu servisler içerisinde de örneğin<strong> HTTP Get</strong> taleplerinin karışılığında çeşitli tipte koleksiyonları döndüren operasyonlarımız olduğunu farz edelim. Bu durumda <strong>global.asax</strong> dosyasındaki kodlarda yönlendirme tablosuna ekleyeceğimiz adres bilgilerine göre, gelen talepleri uygun olan servislere yöndermemiz mümkün olacaktır. Bunu zaten daha önceki bir yazımızda incelemiştik.</p>
<p>Söz gelimi <strong>http://makineadı:port</strong><strong> numarası/CompanyServices/AdventureWorks/Products</strong> ile <strong>http://makineadı:port numarası/CompanyServices/Chinook/Albums</strong> gibi iki talep gönderildiğini düşünelim. Bu taleplerin aynı web uygulamasından host edilen iki farklı servis tipi tarafından değerlendirildiği bir durumda, doğru yönlendirme tekniği ile uygun olan servis ve operasyonunun çağırılması mümkündür. Oysaki istemciler <strong>http://makineadı:port/CompanyServices/</strong> adresine de talepte bulunulabilir. Böyle bir durumda ne olur? Gelin bunu açıklamak için aşağıdaki örnek servis sınıflarını içeren bir <strong>WCF REST Service Application </strong>projemiz olduğunu düşünelim.</p>
<p><strong>AdventureWorksService</strong> <strong>sınıfı;</strong></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
namespace Lesson8
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class AdventureWorksService
{
[WebGet(UriTemplate = "Products")]
public List<Product> GetProducts()
{
return new List<Product>
{
new Product{ Id=1, Name="Büyüteç", ListPrice=1.24M},
new Product{ Id=2, Name="Stabilo Pen 68", ListPrice=2.35M},
new Product{ Id=3, Name="Temizleme Spreyi", ListPrice=4.19M}
};
}
}
}</pre>
<p>Söz konusu sınıf içerisinde yer alan <strong>GetProducts</strong> isimli operasyon geriye <strong>Product</strong> tipinden bir ürün listesi döndürmekle görevlendirilmiştir. Tahmin edeceğiniz üzere şu anda kendi kendimizi yetiştirmeye çalıştığımızdan sadece anlamsız bir liste üretimi söz konusudur.</p>
<p><strong>ChinookService sınıfı;</strong></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
namespace Lesson8
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ChinookService
{
[WebGet(UriTemplate = "Artists")]
public List<Artist> GetArtists()
{
return new List<Artist>
{
new Artist{ Id=1, Name="Ayrosimit",IsGroup=true},
new Artist{ Id=2, Name="Megadet",IsGroup=true},
new Artist{ Id=3, Name="Metalika",IsGroup=true},
new Artist{ Id=4, Name="Co Satriani",IsGroup=false}
};
}
}
}</pre>
<p><strong>ChinookService</strong> sınıfıda, <strong>AdventureWorksService</strong> tipine benzer bir şekilde ama bu kez <strong>Artist</strong> tipinden liste döndüren tek bir operasyon içermektedir. Buraya kadar zaten bir sorun bulunmamaktadır. Ancak aynı web uygulamasında birden fazla servisi host etmek istediğimizde, <strong>RouteTable</strong> nesnesinin <strong>Routes </strong>koleksiyonu içerisinde gerekli düzenlemelerin de yapılması gerekmektedir. Bu nedenle <strong>global.asax.cs</strong> içeriğinin aşağıdaki gibi olduğunu düşünebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.ServiceModel.Activation;
using System.Web;
using System.Web.Routing;
namespace Lesson8
{
public class Global
: HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("AdventureWorks", new WebServiceHostFactory(), typeof(AdventureWorksService)));
RouteTable.Routes.Add(new ServiceRoute("Chinook", new WebServiceHostFactory(), typeof(ChinookService)));
}
}
}</pre>
<p>Buna göre istemciden gelecek olan <strong>http://localhost:10843/CompanyServices/AdventureWorks/Products </strong>ve <strong>http://localhost:10843/CompanyServices/Chinook/Artists </strong>talepleri sorunsuz bir şekilde karşılanacaktır. Ancak doğrudan <strong>Web </strong>uygulamasının <strong>Root </strong>adresine yapılan <strong>http://localhost:10843/CompanyServices/</strong> gibi bir talepte aşağıdaki ekran görüntüsü ile karşılaşılacaktır.</p>
<p><img src="/pics/2010%2f2%2fblg146_Begining.gif" alt="" /></p>
<p>Elbette <strong>Help</strong> sayfalarına gidilerek servislere nasıl talepte bulunulabileceği öğrenilebilir. Ancak elimizde iki servis olduğundan söz konusu yardım sayfalarına gitmek için <strong>http://localhost:10843/CompanyServices/AdventureWorks/help </strong>veya <strong>http://localhost:10843/CompanyServices/Chinook/help </strong>gibi taleplerinin gönderilmesi gerekmektedir.</p>
<p>Sanıyorum ki nihayet ne yapmak istediğimize gelebildik. İstediğimiz şey <strong>http://localhost:10843/CompanyServices/ </strong>adresine yapılan talep ile Web uygulamasından sunulan servisleri bildirmek olacak. Üstelik bu talebe karşılık dönecek mesajın içeriğini kendimiz tasarlayacağız. Yapabilir miyiz? Evet yapabiliriz. Çünkü gerekli tüm tipler <strong>Framework</strong> içerisinde çoktandır mevcutlar. Özetle talebe uygun bir formatta<em>(XML, JSON, ATOM gibi)</em> kendi veri yayınımızı yapacağımızı ifade edebiliriz.</p>
<p>İşe ilk olarak yeni bir servis sınıfını geliştirerek başlamamız gerekiyor. Bu sınıf içerisinde yer alan operasyonumuz geriye, <strong>System.ServiceModel.Channels </strong>isim alanında yer alan <strong>Message </strong>tipinden bir değer döndürüyor olacak. İşte <strong>EntranceService</strong> isimli yeni sınıfımızın içeriği;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.ServiceModel.Syndication;
using System.ServiceModel.Web;
namespace Lesson8
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class EntranceService
{
// İstemci tarafına sunulacak olan kaynakların listesi Resource tipinden generic bir koleksiyonda tutulur.
public static readonly List<Resource> Resources = new List<Resource>
{
new Resource{ Name="Chinook List", Description="Chinook nesneleri listesi", RequestUri=new Uri("Chinook",UriKind.Relative),HelpUri=new Uri("Chinook/help",UriKind.Relative)},
new Resource{ Name="Adventure Works List", Description="Adventure Works nesneleri listesi", RequestUri=new Uri("AdventureWorks",UriKind.Relative),HelpUri=new Uri("AdventureWorks/help",UriKind.Relative)}
};
[WebGet(UriTemplate="")]
public Message GetResources()
{
WebOperationContext optContext = WebOperationContext.Current;
IncomingWebRequestContext incomingRequest = optContext.IncomingRequest;
string mType=string.Empty;
foreach (var acceptType in incomingRequest.GetAcceptHeaderElements())
{
// Gelen talebin Header bilgisinde yer alan MediaType değerine göre geriye Xml, Json veya Atom formatında bir içerik döndürülmesi sağlanır.
mType= acceptType.MediaType.ToLower();
if (mType == "application/xml" || mType == "text/xml")
return optContext.CreateXmlResponse(Resources); // Xml formatında dönüş
else if (mType == "application/json")
return optContext.CreateJsonResponse(Resources);
else if (mType == "application/atom+xml") // Json formatında dönüş
{
// Atom formatında dönüş için SyndicationFeed nesnesinin örneklenmesi gerekmektedir. Resource değişkeninin işaret ettiği koleksiyon bu nesne örneği içerisindeki Items koleksiyonuna atanır
return optContext.CreateAtom10Response(
new SyndicationFeed(
"Company Services Resources",
"Adventure Works & Chinook Kaynakları",
new Uri("", UriKind.Relative),
Resources.Select(r => new SyndicationItem(r.Name, r.Description, r.RequestUri)
)));
}
}
// Varsayılan olarak çıktı XML formatında verilir
return optContext.CreateXmlResponse(Resources);
}
}
// Servisten sunulan servislerin birer kaynak olduğu düşünüldüğünde bu servislere ait ad, açıklama, root Uri ve help page uri bilgilerinin saklandığı tip
public class Resource
{
public string Name { get; set; }
public string Description { get; set; }
public Uri RequestUri { get; set; }
public Uri HelpUri { get; set; }
}
}</pre>
<p><strong>GetResources</strong> isimli servis operasyonunun en önemli özelliği <strong>WebGet</strong> niteliğinde boş bir template kullanılması ve tabiki geriye <strong>Message</strong> tipinden bir değer döndürmesidir. Servis operasyonu dikkatlice incelendiğinde, istemciden gelecek olan taleplere ait <strong>Header</strong> kısımlarında yer alan mesaj formatı bilgisine göre bir çıktı üretildiği görülebilir. Buna göre standart olarak <strong>XML, JSON</strong> ve <strong>ATOM</strong> formatlarında bir üretim söz konusudur. <strong>ATOM</strong> formatındaki çıktının hazırlanması sırasında bir <strong>SyndicationFeed</strong> nesnesinin örneklendiğine dikkat edilmelidir. Tüm bu formatlama işlemlerinde o anki <strong>Web</strong> içeriği referansını taşıyan <strong>WebOperationContext</strong> tipine ait <strong>Create...</strong> metodlarından yararlanılmaktadır.</p>
<p>Tabi işimiz bu servis sınıfını yazmakla bitmiş değil. Fark ettiğiniz üzere üçüncü bir servis sınıfımız oldu ve bu sınıfa gelen talepler <strong>UriTemplate</strong> bilgisine göre <strong>Web</strong> uygulamasının <strong>Root </strong>adresine yapılmakta. Dolayısıyla boş template için gerekli yönlendirme bilgisinin<strong> global.asax.cs</strong> içerisinde bildirilmesi gerekiyor. Aynen aşağıda olduğu gibi.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.ServiceModel.Activation;
using System.Web;
using System.Web.Routing;
namespace Lesson8
{
public class Global
: HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("", new WebServiceHostFactory(), typeof(EntranceService)));
RouteTable.Routes.Add(new ServiceRoute("AdventureWorks", new WebServiceHostFactory(), typeof(AdventureWorksService)));
RouteTable.Routes.Add(new ServiceRoute("Chinook", new WebServiceHostFactory(), typeof(ChinookService)));
}
}
}</pre>
<p>Buna göre örnek bir tarayıcı uygulama üzerinden <strong>http://localhost:10843/CompanyServices/</strong> adresine yapacağımız bir talebin sonucu aşağıdaki gibi olacaktır.</p>
<p><img src="/pics/2010%2f2%2fblg146_Last.gif" alt="" /></p>
<p>Her şey yolunda görünüyor. Ancak ufak bir pürüz var. <strong>EntranceService</strong> tipine boş <strong>Uri</strong> bilgisi üzerinden bir başka deyişle <strong>Web</strong> uygulamasına ait <strong>Root</strong> adresten gidilebildiği için, <strong>http://localhost:10843/CompanyServices/help</strong> şeklinde gönderilen bir talepte aşağıdaki ekran görüntüsü ile karşılaşılacaktır.</p>
<p><img src="/pics/2010%2f2%2fblg146_HelpPage.gif" alt="" /></p>
<p>Oysaki bu <strong>Help</strong> sayfasının çıkmasına pekte gerek yoktur. Bu bir zorunluluk değildir ama olmasının da bir anlamı yoktur. Dolayısıyla pasif hale getirmemiz gerekmektedir. Bu amaçla <strong>WCF 4.0</strong> ile birlikte konfigurasyon dosyasına getirilen yeniliklerden yararlanarak gerekli sonucu elde edebiliriz. Tek yapmamız gereken<strong> Web.config</strong> dosyasını aşağıdaki gibi düzenlemek.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</modules>
</system.webServer>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<!-- Diğer servislerin help sayfalarının disable olmaması için ilk standartEndpoint elementine dokunulmamalıdır-->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
<standardEndpoint name="EntranceServiceEndPoint"/><!-- Varsayılan olarak helpEnabled özelliği false değere sahiptir-->
</webHttpEndpoint>
</standardEndpoints>
<services>
<service name="Lesson8.EntranceService">
<endpoint contract="Lesson8.EntranceService" kind="webHttpEndpoint" endpointConfiguration="EntranceServiceEndPoint"/>
</service>
</services>
</system.serviceModel>
</configuration></pre>
<p>Zaten varsayılan <strong>web.config</strong> dosyası içeriğine göre, tüm servis talepleri otomatik olarak standart bir <strong>Endpoint </strong>tipine yönlendirilir. Ancak senaryomuza göre ana adres üzerinden yapılan yardım sayfası talebi geçersiz olmalı, diğerleri ile kullanılabilir durumda kalmalıdır. Bu nedenle <strong>EntranceService</strong> isimli hizmet için de bir <strong>Endpoint</strong> tanımlaması yapılmış ve <strong>webHttpEndpoint</strong> içerisindeki farklı bir ayara yönlendirilmiştir. Buradaki düzenlemeye göre <strong>EntranceService</strong> dışındaki tüm servislerin help sayfalarına ulaşılabilmektedir. Ancak <strong>EntranceService</strong> için help sayfası <span style="text-decoration: underline;">gösterilmemektedir</span>. Buna göre <strong>http://localhost:10843/CompanyServices/help</strong> adresine bir talepte bulunulduğunda aşağıdaki ekran görüntüsü ile karşılaşılacaktır.</p>
<p><img src="/pics/2010%2f2%2fblg146_HelpDisabled.gif" alt="" /></p>
<p>Servis tarafında pek çok işimizi hallettik. Ancak test etmemiz gereken bir husus daha var. İstemcinin, <strong>XML</strong> dışında <strong>ATOM</strong> veya <strong>JSON</strong> formatlı mesaj taleplerine karşılık olarak nasıl sonuçlar alacağı. Nitekim tarayıcı üzerinden yaptığımız taleplerde standart olarak <strong>XML</strong> çıktısı aldığımızı gördük ve biliyoruz. Peki ya diğer formatlar? Bu durumu test etmek için yine basit bir <strong>Console</strong> uygulaması geliştiriyor olacağız. Her zamanki gibi <strong>HttpClient</strong> tipinden yararlanacağız. Bu sebepten <strong>REST Starter Kit</strong> ile gelen <strong>Microsoft.Http </strong>ve <strong>Microsoft.Http.Extensions Assembly </strong>referanslarını eklemeyi unutmayalım. İşte <strong>Console</strong> uygulamamıza ait kodlarımız.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using Microsoft.Http;
namespace ClientApp
{
class Program
{
static void Main(string[] args)
{
using (HttpClient client = new HttpClient("http://localhost:10843/CompanyServices/"))
{
Execute(client,"application/json"); // Json formatında talep gönderilir
Execute(client, "application/atom+xml"); // atom formatında talep gönderilir
Execute(client, "noFormat"); // Olmayan bir format için talep gönderilir
}
}
private static void Execute(HttpClient client,string acceptFormat)
{
// HttpRequestMessage nesnesi örneklenirken kullanılan ikinci parametreye göre EntranceService tarafından karşılanacak bir talep oluşturulur
using (HttpRequestMessage requestMessage = new HttpRequestMessage("GET", String.Empty))
{
// Accept özelliğinin Add metodu yardımıyla Header' a eklenen bilgiye göre hangi formatta mesaj istendiğin belirtilir.
requestMessage.Headers.Accept.Add(acceptFormat);
using (HttpResponseMessage responseMesssage = client.Send(requestMessage))
{
Console.WriteLine("\n{0}\n", responseMesssage.Content.ReadAsString());
}
}
}
}
}</pre>
<p>Kodlarımızı çalıştırdığımızda aşağıdaki ekran görüntüsünde yer alan sonuçları elde ederiz.</p>
<p><img src="/pics/2010%2f2%2fblg146_RuntimeLast.gif" alt="" /></p>
<p>Vuuuvvvv!!! Şu anda masamdaki şekerlerin oranına bakıyorum da...Baya bir tüketmişim. <img title="Sealed" src="/editors/tiny_mce3/plugins/emotions/img/smiley-sealed.gif" alt="Sealed" border="0" /> Artık dinlenmeye çekilmenin vakti geldi sanırım. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2010%2f2%2fLesson8_RC.rar">Lesson8_RC.rar (181,52 kb)</a> <strong>[Örnek Visual Studio 2010 </strong><strong>Ultimate </strong><strong>Beta 2 Sürümünde geliştirilmiş ancak RC sürümü üzerinde de test edilmiştir]</strong></p>2010-04-08T07:39:00+00:00wcfwcf 4.0webhttp servicesrestfulnon soapwcf webhttp servicesbsenyurtBu yazımızda, son günlerde sıklıkla üzerinde durduğumuz WCF WebHttp Service' lerinde, istemciden gelen root adres bazlı taleplerin nasıl karşılanacağını ve özel formatta mesajların nasıl döndürüleceğini incelemeye çalışıyor olacağız.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=b61f85f1-8c35-4899-942f-589119e179ac0https://www.buraksenyurt.com/trackback.axd?id=b61f85f1-8c35-4899-942f-589119e179achttps://www.buraksenyurt.com/post/WCF-WebHttp-Services-Using-Custom-Message#commenthttps://www.buraksenyurt.com/syndication.axd?post=b61f85f1-8c35-4899-942f-589119e179ac