Makale Özeti

Standart web sayfalarına programsal olarak veri göndermek kolay olsa da iş bir asp.net sayfasına gelince herşey değişiyor ve zorluklar başlıyor. Bu makalemde sizlerle bir asp.net sayfasına nasıl veri göndereceğinizi püf noktalarıyla birlikte paylaşıyorum.

Makale

Kimi zaman yazmış olduğunuz bir web sitesini test etmek isteyebilirsiniz. Bu testin amacı sayfanın beklenildiği gibi çalıştığını otomatik olarak doğrulamak olabileceği gibi, web sitenizi bir yük testine tabii tutarak gerçek ortamda kaldırabileceği yükü görmek de olabilir. Bu makalemde sizlerle asp.net ile yazılmış bir web sitesine C# ile nasıl veri gönderilebileceğini ve test edilebileceğini paylaşacağım.

HTTP protokolü üzerinden bir web sayfasına HEAD, GET, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH yöntemleri kullanılarak veri gönderilebilir. Bu yöntemler arasında en sık tercih edilenler ise GET ve POST’tur. Standart bir asp.net web sayfası, form verilerini bu yöntemlerden POST’u kullanarak göndermektedir.

HTTP POST metodunda veri sunucuya isteğin bir parçası olarak iletilmektedir.Verinin sadece URL’nin bir parçası olarak iletildiği GET metodundan farklı olarak POST metodunda istek içerisinde mesaj vucudu (message body) da yer almaktadır; ki bu da değişken bir boyutta ve formattaki verinin kolaylıkla sunucuya iletilebilmesine olanak sağlamaktadır.

Web sayfası içerisinden yapılan standart bir POST isteği genellikle "application/x-www-form-urlencoded" içerik türüne sahiptir ve gönderilen her bir anahtar-değer ikilisi birbirinden "&" karakteri ile ayrılmaktadır. Anahtar ve değer arasında ise "=" karakteri yer almaktadır. Gönderilen veri içerisindeki boşluklar "+" karakteri ile yer değiştirdikten sonra da kalan diğer karakterlere URL encoding uygulanır.

Anahtar Değer
Adi Ali Veli
Soyadi Örnek
Sehir İzmir
Ulke Türkiye

Yukarıdaki örnek anahtar-değer ikilileri için oluşturulacak olan POST istek içeriğini aşağıda bulabilirsiniz;

Adi=Ali+Veli&Soyadi=%c3%96rnek&Sehir=%c4%b0zmir&Ulke=T%c3%bcrkiye

Görüldüğü gibi Türkçe karakterler de encode edilerek POST içeriğine eklenmiş durumda.

Pek çok web sayfasına C# kullanarak otomatik veri göndermek aslında kolay bir iştir; HttpWebRequest sınıfı vasıtasıyla bir istek oluşturarak ve sayfanın adresini vererek göndermeniz yeterli olmakta. Temelde ASP.Net sayfalarına veri gönderimi de aynı yolu izliyor olmasına karşın başarılı bir gönderim için bilmeniz gereken ufak bir kaç püf noktası bulunmakta.

ASP.Net web formunun geri bildirimlerde (PostBack) kontrollerin durumunu koruyabilmek/takip edebilmek adına view state yöntemini kullanmaktadır. Kontrollarin geri bildirimlerde hatırlanması istenen özellikleri form içerisinde gizli bir değişkende saklanmaktadır. Her geri bildirim de bu değişken değeri asp.net tarafından okunarak kontrollerin durumları yeniden ayarlanır. Form içerisinde view state’i tutan bu gizli olarak konumlanmış olan değişkeninin adı ise  __VIEWSTATE’tir. Programsal olarak yapılacak bir form gönderiminde form içerisindeki alanlara ait değerleri göndermenin yanı sıra bu değişkeninde gönderilmesi gerekmektedir; aksi takdirde gönderimimiz başarısız olacaktır.

Yukarıdaki bilgiler ışığında, bir asp.net web sayfasına programsal olarak veri gönderebilmek için öncelikle sayfanın url’si ile birlikte bir HttpWebRequest oluşturmalıyız;

var webRequest = (HttpWebRequest)WebRequest.Create("[WEB SAYFASININ ADRESI]");

Web request'im method olarak HTTP POST'u kullanmalı;

webRequest.Method = "POST";

İçerik türü olarak application/x-www-form-urlencoded kullanılmalı;

webRequest.ContentType = "application/x-www-form-urlencoded";

Göndermek istediğiniz string formatındaki veriyi byte dizisi haline çevirmelisiniz;

var tampon = Encoding.UTF8.GetBytes("[GÖNDERİLECEK İÇERİK]");

Göndereceğimiz içeriğin boyutu belirtilmeli;

webRequest.ContentLength = tampon.Length;

Son olarak da veri istek stream’i içerisine yazılmalı ve yanıt okunmalıdır;

using (var istekStream = webRequest.GetRequestStream()) {
   istekStream.Write(tampon, 0, tampon.Length);
}

var yanit = (HttpWebResponse)webRequest.GetResponse();

var yanitIcerigi = string.Empty;
using (var responseStream = yanit.GetResponseStream()) {
   var streamReader = new StreamReader(responseStream);
   yanitIcerigi = streamReader.ReadToEnd();
}

Tüm kodu bir araya toparlayacak olursak;

var tampon = Encoding.UTF8.GetBytes(gonderilecekVeri);
var webRequest = (HttpWebRequest)WebRequest.Create("[WEB SAYFASININ ADRESI]");

webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = tampon.Length;
webRequest.Proxy = new WebProxy((string)null, true);

using (var istekStream = webRequest.GetRequestStream()){
   istekStream.Write(tampon, 0, tampon.Length);
}

var yanit = (HttpWebResponse)webRequest.GetResponse();

var yanitIcerigi = string.Empty;
using (var responseStream = yanit.GetResponseStream()) {
   var streamReader = new StreamReader(responseStream);
   yanitIcerigi = streamReader.ReadToEnd();
}

Buraya kadar anlattıklarım işin kolay kısmıydı aslına bakarsanız; bu yöntemle herhangi bir web sayfasına HTTP POST metodu ile veri gönderebilirsiniz; fakat asp.net sayfalarına göndereceğiniz veri içerisinde view state’de yer almalıdır. Rastgelen oluşturacağınız bir view state değerinin işinizi hiç bir şekilde çözmeyeceğini baştan söylemeliyim. Peki bu durumda view state değeri nasıl oluşturabilirim? Bu sorunun yanıtı aslında basit; view state değerini siz oluşturmayacaksınız.

Web tarayıcınız üzerinden dolaştığınız, formlarını doldurduğunuz bir asp.net sayfasını düşünün. Böyle bir sayfada ilk isteğinizle birlikte bir view state değeri oluşturularak form içerisindeki gizli __VIEWSTATE alanına değer olarak atanır, siz sayfadaki kontrollerin değerlerini değiştirdikçe, formları gönderdikçe bu gizli alan da gideceği için asp.net hatasız şekilde view state’i yönetecektir.

Programsal olarak bir asp.net sayfasına veri bildirimi yapmak istiyorsak, yapmamız gereken de aslında tam olarak yukarıda örneklediğim web tarayıcısı davranışı taklit etmektir. İlk önce bir istek yaparak sayfanın ve view state’in oluşmasını sağlamalı, ardından bu değerleri kullanarak form’u doldurup göndermeliyiz.

private string IlkIstek(string sayfaAdresi){
   var webClient = new WebClient();

   using (var stream = webClient.OpenRead(sayfaAdresi)){
      var streamReader = new StreamReader(stream);

      return streamReader.ReadToEnd();
   }
}

IlkIstek metodu bizim için verdiğimiz bir adrese istek yaparak gelen yanıtı iletmekte. Aşağıdaki AlanDegeriniBul metodu kullanılarak bu yanıt içerisindeki istenen herhangi bir input alanı değeri alınabilir.

private string AlanDegeriniBul(string sayfaIcerigi, string alanAdi){
   var pattern = "<input.*id=\"" + alanAdi + "\".*value=\"(?<deger>.*?)\".*/>";

   var regex = new Regex(pattern, RegexOptions.IgnoreCase);
   var match = regex.Match(sayfaIcerigi);

   if (match != null && match.Groups["deger"].Success && match.Groups.Count > 0) {
      return match.Groups["deger"].Value;
   }

   return string.Empty;
}

AlanDegeriniBul metodu verilen alan adının değerini bulmak için sayfa içeriğinde bir düzenli ifade araması yapmaktadır. Verilen ifadeye uygun bir grup bulunması durumunda değer dönmekte, aksi durumlarda ise boş bir string dönmektedir.

IlkIstek ve AlanDegeriniBul metodlarının aşağıdaki şekilde kullanılması sonucunda ihtiyacımız olan view state değerine ulaşmış olacağız.

var ilkSayfaIcerigi = IlkIstek("[WEB SAYFASININ ADRESI]");
var viewState = AlanDegeriniBul(ilkSayfaIcerigi, "__VIEWSTATE");

Elde ettiğimiz bu view state değerini de önceki alanların sonuna ekleyerek isteğimizi gerçekleştirebiliriz.

var ilkSayfaIcerigi = IlkIstek("[WEB SAYFASININ ADRESI]");
var viewState = AlanDegeriniBul(ilkSayfaIcerigi, "__VIEWSTATE");

var tampon = Encoding.UTF8.GetBytes(gonderilecekVeri + "&__VIEWSTATE=" +      HttpUtility.UrlEncode(viewState));
var webRequest = (HttpWebRequest)WebRequest.Create("[WEB SAYFASININ ADRESI]");

webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = tampon.Length;
webRequest.Proxy = new WebProxy((string)null, true);

using (var istekStream = webRequest.GetRequestStream()){
   istekStream.Write(tampon, 0, tampon.Length);
}

var yanit = (HttpWebResponse)webRequest.GetResponse();

var yanitIcerigi = string.Empty;
using (var responseStream = yanit.GetResponseStream()){
   var streamReader = new StreamReader(responseStream);
   yanitIcerigi = streamReader.ReadToEnd();
}

Bu yöntem kullanılarak olay doğrulama (event validation) özelliği aktif edilmemiş tüm asp.net sayfalarına veri bildirimi yapabilirsiniz.

Olay doğrulama özelliği açılmış asp.net sayfalarında yukarıdaki koda ufak bir ekleme yaparak asp.net’in olay doğrulamasınca beklenen __EVENTVALIDATION alan değerini de bulup göndermemiz gerekli.

Asp.Net oturumları takip edebilmek için her bir oturuma özel tekil bir değeri istemciye çerez olarak iletmekte ve bir sonraki istekte bu bilgiye göre ilgili oturumda işlem yaptırmaktadır. Bu durumda oturum bilgisinin tutulduğu ve oturum gerektiren sayfalara veri gönderebilmek için bir CookieContainer oluşturup istekle ilişkilendirmelisiniz.

var ilkSayfaIcerigi = IlkIstek("[WEB SAYFASININ ADRESI]");

var viewState = AlanDegeriniBul(ilkSayfaIcerigi, "__VIEWSTATE");
var eventValidation = AlanDegeriniBul(ilkSayfaIcerigi, "__EVENTVALIDATION");

var tampon = Encoding.UTF8.GetBytes(gonderilecekVeri +
string.Format("&__VIEWSTATE={0}&__EVENTVALIDATION={1}",
     HttpUtility.UrlEncode(viewState),
     HttpUtility.UrlEncode(eventValidation));

webRequest = (HttpWebRequest)WebRequest.Create("[WEB SAYFASININ ADRESİ]");

webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = tampon.Length;
webRequest.Proxy = new WebProxy((string)null, true);
webRequest.CookieContainer = new CookieContainer();

using (var istekStream = webRequest.GetRequestStream()) {
   istekStream.Write(tampon, 0, tampon.Length);
}

var yanit = (HttpWebResponse)webRequest.GetResponse();

var yanitIcerigi = string.Empty;
using (var responseStream = yanit.GetResponseStream()) {
   var streamReader = new StreamReader(responseStream);
   yanitIcerigi = streamReader.ReadToEnd();
}

Yukarıda yer alan C# kodu yardımıyla bir asp.net sayfasına veri gönderebiliyor olsakta, tam bir etkileşim yapabilmek için (örneğin bir butona basılması) takip etmemiz gereken bir kaç adım daha bulunmakta.

Asp.Net web sayfaları, form üzerindeki bir butona basılması gibi, olay geri bildirimlerini sayfa içerisinde tanımlı olan __doPostBack javascript metodunu kullanarak yapmaktadır. Bu javascript metodunun kodunu inceleyecek olursak;

function __doPostBack(eventTarget, eventArgument) {
   if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
      theForm.__EVENTTARGET.value = eventTarget;
      theForm.__EVENTARGUMENT.value = eventArgument;
      theForm.submit();
   }
}

__doPostBack metodu eventTarget ve eventArgument adıyla iki parametre kabul etmekte. Bu parametrelerden eventTarget parametresi tetikleyici form bileşenini belirtirken (örneğin; butona basıldı ise basılan butonun adı), eventArgument parametresi olay argumanlarını belirtmektedir. Bu parametreler metod içerisinde sırasıyla __EVENTTARGET ve __EVENTARGUMENT form alanlarına atanmakta, ardından da form’un sunucuya gönderimi yapılmakta.

Kod içerisinden __EVENTTARGET parametresine geçmemiz gereken değeri asp.net web sayfasını bir browserdan açarak görebiliriz. Sayfanın kaynak kodunu açarak tetikleyici bileşenin adını (örneğin bir tıklama olayı için tıklanacak butonun adı) kopyalanmalı.

Aşağıda şimdiye kadar anlattıklarımdan yola çıkarak Visual Studio 2010 ASP.Net Web Application şablonu ile oluşturulmuş bir web uygulamasına veri göndererek sisteme girişi sağlayan örnek bir uygulama kodunu bulabilirsiniz.

var ilkSayfaIcerigi = IlkIstek("http://localhost:35836/Account/Login.aspx");

var viewState = AlanDegeriniBul(ilkSayfaIcerigi, "__VIEWSTATE");
var eventValidation = AlanDegeriniBul(ilkSayfaIcerigi, "__EVENTVALIDATION");

var gonderilecekVeri = string.Format("{0}={1}",
     HttpUtility.UrlEncode("ctl00$MainContent$LoginUser$UserName"),
     HttpUtility.UrlEncode("KULLANICI ADI"));
gonderilecekVeri += string.Format("&{0}={1}",
     HttpUtility.UrlEncode("ctl00$MainContent$LoginUser$Password"),
     HttpUtility.UrlEncode("KULLANICI ŞYFRESY"));
gonderilecekVeri += string.Format("&{0}={1}",
     HttpUtility.UrlEncode("__EVENTTARGET"),
     HttpUtility.UrlEncode("ctl00$MainContent$LoginUser$LoginButton"));
gonderilecekVeri += string.Format("&{0}={1}",
     HttpUtility.UrlEncode("__EVENTARGUMENT"),
     HttpUtility.UrlEncode(string.Empty));
gonderilecekVeri += string.Format("&{0}={1}",
     HttpUtility.UrlEncode("__VIEWSTATE"),
     HttpUtility.UrlEncode(viewState));
gonderilecekVeri += string.Format("&{0}={1}",
     HttpUtility.UrlEncode("__EVENTVALIDATION"),
     HttpUtility.UrlEncode(eventValidation));

var tampon = Encoding.UTF8.GetBytes(gonderilecekVeri);

webRequest = (HttpWebRequest)WebRequest.Create("http://localhost:35836/Account/Login.aspx");

webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = tampon.Length;
webRequest.Proxy = new WebProxy((string)null, true);
webRequest.CookieContainer = new CookieContainer();

using (var istekStream = webRequest.GetRequestStream()){
   istekStream.Write(tampon, 0, tampon.Length);
}

var yanit = (HttpWebResponse)webRequest.GetResponse();

var yanitIcerigi = string.Empty;
using (var responseStream = yanit.GetResponseStream()){
   var streamReader = new StreamReader(responseStream);
   yanitIcerigi = streamReader.ReadToEnd();
}

Bu örnek veri gönderim kodu, içeriği hiç değiştirilmemiş ve 35836 portundan hizmet veren bir ASP.Net Web Application için hazırlanmıştır.


Fatih Boy

http://www.enterprisecoding.com
http://twitter.com/fatihboy