Dotnet Core HttpClient Kullanımı

29 Eki

Dotnet core veya .Net Framework uygulamalarında bir Web servisine erişmek için HttpClient sınıfından bir nesne oluşturulur ve kullanılır. Buraya kadar her şey normal. İşlem bittikten sonra kullanılan bu nesne yok edilir. İşte bu durumda işler biraz karışmaya başlar.

HttiClinet using blok ile kullanımı

public class ToDoClient : IToDoClient
{
	public async Task<Todo> Get(int id)
	{
		using(var client = new HttpClient())
		{
			BaseAddress = new Uri(
				"https://jsonplaceholder.typicode.com");
		}
		
		return await client.GetFromAsync<ToDo>(
			$"/todos/{id}");
	}
}

HttpClient sınıfı, IDisposible interface uyguladığı için, using bloğu ile kullanılabilir. Using bloğunda oluşturulan nesneler, blok sonunda ortadan kaldırılır. Ancak nesnenin kullandığı soket hemen serbest bırakılmaz. Ağır yük altında kullanılabilir soket sayısı azalır hatta tükenebilir. Burada konu ile ilgili bir yazı bulunmaktadır.

Yeniden kullanılabilir HttpClient nesnesi

Bu durumun önüne geçmek için tek bir kez singleton olarak oluşturulan ve paylaşılan bir HttpClient nesnesi kullanılabilir.

public class ToDoClient : IToDoClient
{
   private readonly HttpClient client = new ()
	{
		 BaseAddress = new Uri(
			"https://jsonplaceholder.typicode.com");
	};

	public async Task<Todo> Get(int id)
	{		
		return await client.GetFromAsync<ToDo>(
			$"/todos/{id}");
	}
}

Bu çözüm ise, az sayıda kullanılan kısa ömürlü console uygulamaları için uygun olabilir. Ancak geliştiricilerin karşılaştığı diğer bir sorun, uzun süren işlemlerde paylaşılan bir HttpClient örneği kullanırken ortaya çıkar. HttpClient’in tekil veya statik bir nesne olarak başlatıldığı bir durumda, DNS ip değişiklikleri yapılırsa uygulama hata alır. Örneğin load balancer ile bir DNS birden fazla ip de bulunan sunuculara sırayla yönlendiriliyor olabilir.

builder.Services.AddSingleton<IToDoClient, ToDoClient>();

Bu arada ToDoClient Program.cs içerisinde singleton olarak tanımlanabilir. Eğer Transient veya Scoped olarak kullanılırsa her request sırasında yeniden nesne oluşturacaktır. Bu durumun önüne geçmek için HttpCleint nesnesi static olarak tanımlanabilir.

public class ToDoClient : IToDoClient
{
    private static readonly HttpClient client = new ()
	{
		 BaseAddress = new Uri(
			"https://jsonplaceholder.typicode.com");
	};

	public async Task<Todo> Get(int id)
	{		
		return await client.GetFromAsync<ToDo>(
			$"/todos/{id}");
	}
}

Ancak bu durumda BaseAddress ile tanımlanan DNS TTL süresi bittiğinde domain adı yeni bir ip adresini işaret eder. Ancak kod restart olmadan yeni ip adresini bilemez. Çünkü default HttpClient içerisinde bunu yakalayan bir mekanizma bulunmaz. Bu durumun yakalayabilmek için SockeHttpHandler kullanılabilir.

public class ToDoClient : IToDoClient
{
	private SocketsHttpHandler socketHandler = new()
	{
		PooledConnectionLifetime = TimeSpan.FromMinutes(5)
	};
	
    private static readonly HttpClient client = new (socketHandler)
	{
		 BaseAddress = new Uri(
			"https://jsonplaceholder.typicode.com");
	};

	public async Task<Todo> Get(int id)
	{		
		return await client.GetFromAsync<ToDo>(
			$"/todos/{id}");
	}
}

Ancak bu yöntem de en iyi çözüm yolu değildir. En iyi çözüm yolu  .NET Core 2.1 ile birlikte gelen IHttpClientFactory interface kullanmaktır.

IHttpClientFactory Kullanımı

Bu interface kullanmadan önce Program.cs içerisinde bir tanımlama yapılması gerekmektedir.

builder.Services.AddSingleton<IToDoClient, ToDoClient>();
builder.Services.AddHttpClient<IToDoClient, ToDoClient>(client =>
{
   client.BaseAddress = new Uri(
			"https://jsonplaceholder.typicode.com");
});

Bu düzenleme ile artık HttpClient aşağıdaki gibi kullanılabilir.

public class ToDoClient : IToDoClient
{
    private readonly HttpClient _httpClient;
	
	public ToDoClient(HttpClient httpClient)
	{
		_httpClient = httpClient;
	}
	
	public async Task<Todo> Get(int id)
	{		
		return await _httpClient.GetFromAsync<ToDo>(
			$"/todos/{id}");
	}
}

Bu durumda HttpClient nesnesi IHttpClientFactory tarafından yönetilmektedir. Çünkü ortak bir havuzdaki HttpMessageHandler nesneleri, birden çok HttpClient örneği tarafından yeniden kullanılabilen nesnelerdir. Bu sayede DNS sorunu çözülmektedir.

Named Client kullanımı

Servis binding sırasında AddHttpClient() metoduna bir isim vererk kullanmak mümkündür.

builder.Services.AddSingleton<IToDoClient, ToDoClient>();
builder.Services.AddHttpClient("TodoApi",client =>
{
   client.BaseAddress = new Uri(
			"https://jsonplaceholder.typicode.com");
});
public class ToDoClient : IToDoClient
{
    private readonly IHttpClientFactory _httpClientFactory;
	
	public ToDoClient(IHttpClientFactory httpClientFactory)
	{
		_httpClientFactory = httpClientFactory;
	}
	
	public async Task<Todo> Get(int id)
	{		
	    var client = _httpClientFactory.CreateClient("ToDoApi");
		
		return await client.GetFromAsync<ToDo>(
			$"/todos/{id}");
	}
}

Bu sayede doğrudan IHttpClientFactory interface nesneleri ile HttpClient request’leri en verimli şekilde gerçekleştirilebilmektedir.

Kaynaklar

  • https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
  • https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Asp.Net 5 verisyon numaralandırması Asp.Net Core 1.0 olarak değişiyor

11 Şub

Microsoft, son yıllarda yaptığı değişiklikle platform bağımsız teknolojiler geliştirmeye başlamıştır. Artık Microsoft ürünlerinin sadece Windows işletim sistemlerinde değil Linux ve Mac OS gibi işletim sistemlerinde de çalışabilmesi amaçlanmaktadır. Bu demek oluyor ki Microsoft geliştirme altyapısında köklü bir değişiklik yapılmaktadır. Artık biz geliştiricilere açık kaynak bir Microsoft platformu sunulmaktadır.

Bilindiği Asp.Net Framework sürüm serisi 1.0, 2.0, … ve 4.5 şeklinde devam etmekteydi. Bu sürümler üzerinde geliştirilen Web uygulamalarının yayınlanması IIS(Internet Information Services) üzerinden yapılmaktadır. Asp.Net 4.6 ve 5 sonrası için platform bağımsız bir yapının planlanması ile yeni versiyonun major numarasını değiştirerek yola devam etmek anlamsız olacaktı. Bu sebepten dolayı sürümler yeniden adlandırıldı.

Yapılan Yeni İsimlendirmeler

  • Asp.Net 5 yerine Asp.Net Core 1.0
  • .Net Core 5 yerine .Net Core 1.0
  • Entity Framework 7 yerine Entity Framework Core 1.0
  • .Net Framework yerine artık .NET Execution Environment (DNX) kullanılmaktadır.

image_60140ec0-ce46-4dbf-a14f-4210eab7f42c

Kaynaklar:

Asp.Net Web Api ile JSON sonuçlarını Camel Case şeklinde göstermek

1 Nis

Asp.Net Web Api uygulamalarında action metodlar tarafından JSON formatında sunulan nesneler, C# ile oluşturulan nesnelerin özellikleri şeklinde oluşturulurlar. Yani C# tarafında oluşturduğumuz bir Product tipi şu şekilde olsun.

public class Product
{
   public string Name { get; set; }
   public string Price { get; set; }
}

Bu tipe ait bir nesneyi veya nesne gurubunu Web Api ile dışarı sunmak için:

public class ProductController:ApiController
{
      public IEnumerable<Product> Get()
      {
         return new List<Product>
         {
         new Product{CategoryName = "Notebook", ListPrice = "1250 TL"},
         new Product{CategoryName = "Tablet", ListPrice = "850 TL"},
         new Product{CategoryName = "Monitor", ListPrice = "350 TL"}
         };
     }
}

Şeklinde bir Controller oluşturabiliriz. Bu şekilde uygulamamızın vereceği cevabı inceleyecek olursak:

Sonuç-1
Sonuç-1

Kategori isimleri CategoryName şeklinde, fiyarlar da ListPrice şeklinde büyük harfle başlatılmış şekilde görünmektedir. Çünkü bu serileştirme işlemi C# ile oluşturduğumuz Product tipinin property üyelerine göre yapılmıştır. Diğer bir deyişle sınıfta belirlediğimiz property isimleri nasıl yazılmış ise JSON property isismleri de aynı şekilde olşuturulmuştur.

Ancak Javascript tarafında gelenek olarak nesne isimleri küçük harfle başlayan ve CamelCasing yapısına uygun olarak kullanılmaktadır. Bu işlemi Newtonsoft.Json.Serialization kütüphanesi bizim yerimize gerçekleştirebilmektedir. Bunun için WebApiConfig dosyasında bir düzenleme yapmak gerekmektedir.

public static void Register(HttpConfiguration config)
{

var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();

jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();}

Tekrar çalıştırdığımızda sonuç:

Sonuç-2
Sonuç-2

Asp.net Globalization Ayarları

18 Tem

Asp.net projelerimizi yayınladığımızda bazen türkçe karakter sorunlarıyla karşılaşabilmekteyiz. Bu yazıda anlatmak istediğim Türkçe karakter sorununda ziyade geliştirme yaptığımız bilgisayarlar ile sunucular arasındaki kültür farkından kaynaklanabilecek sorunlar hakkında olacak.

Örneğin kendi makinamız türkçe işletim sistemli ve sunucumuz da yurt dışında. Bu durumda hem türkçe karakter sorunu, hem de tarihsel farklardan dolayı oluşabilecek tarih format farklılıkları gibi sorunlarla karşılaşabilmekteyiz. Bu durumu aşmak için yapmamız gereken web.config dosyası içerisindeki system.web alanına bir globalization ayarını düzenlemektir.


<system.web>
<globalization culture="tr-TR" />

Bu durumda kültük farkından kaynaklanan sorunlardan kurtulabilmekteyiz.

Eğer ASP.NET MVC projesi ile geliştirme yapıyorsak, Default model binder, kültür farkından dolayı tarih formatlarındaki property’leri set edemiyor. Böyle bir sorunu da bu şekilde aşabilmekteyiz.

Asp.net Mvc Model Binding

5 Eyl

MVC mimarisinde açıklanması gereken bir konu da Model Binding konusudur. Bu yazıda Model Binding konusunu incelemeye çalışacağız.

Tanım: Model Binding HTTP request ile gelen verilerinin ayrıştırılarak ilgili Controller’da bulunan Action metod parametrelerine ve uygun .NET tiplerine dönüştürülmesi işlemidir.

Eğer bir action metod parametre almışsa Model Binding işlemi gerçekleşmektedir.

Arka planda gerçekleşen olaylar: Şimdi aşağıdaki gibi Index adında, string türünde bir tane parametre alan bir Action metod olduğunu varsayalım.

public ActionResult Index(string name)

Index Action’ı çağırılmadan önce arka plan işlemlerini yönetecek iki önemli bileşen vardır. Bunlar ValueProviderDictionary ve DefaultModelBinder’dır.

ValueProviderDictionary: HTTP request ile gelen parametreleri çekerek string halinde depolar. Bu işlem aşağıdaki sırayla gerçekleşir.

  • Request.Form[“name”] var mı diye bakar.
  • Yoksa RouteData.Values[“name”] var mı diye bakar.
  • Yoksa Request.QueryString[“name”] var mı diye bakar.
  • Yoksa null değer döndürür.

Böylece ValueProviderDictionary  HTTP request ile gelen değerleri elde etmiş olur.

DefaultModelBinder: ValueProviderDictionary sınıfının elde ettiği değerleri uygun .NET objelerine gönüştürme görevini üstlenir. Yukardaki örnekte “name” parametresi zaten string tipindedir. Bu durumda DefaultModelBinder dönüşüm yapmak durumunda değildir. Fakat aşağıdaki gibi int veya başka türde parametre alan Action metodlar olabilir.

Durum 1

public ActionResult Index(int id, decimal rate)

Bu durumda DefaultModelBinder sınıfı parametre tip dönüşümlerini otomatik olarak yapacaktır.

Durum 2

Şu ana kadar olan kısımda HTTP request ile gelen isteklerin ayrıştırılıp uygun .NET tiplerine dönüştürüldüğünü gördük. Ancak aşağıdaki gibi özel bir tipin modele bind edilmesi gereken bir durum sözkonusu olabilir.

public ActionResult Index(Person person)

DefaultModelBinder yukarıdaki gibi özel bir .NET tipi ile (Person) karşılaştığında Reflection yardımına koşar. Reflection sayesinde özel tipe ait tüm public özellikler ValueProviderDictionary sınıfı tarafından sağlanan değerleri alır.

Binding

Model binding işlemini bu noktadan itibaren bir örnek yardımıyla incelemeye çalışalım. Örneğimizde bir Person model nesnesinin nasıl bind edildiğini inceliyor olacağız.

public class Person
{
     public int Id { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
}

Autoproperty’ler yardımıyla oluşturulmuş Person model tipimiz yukarıda basit bir şekilde oluşturulmuş durumda.

View tarafında bir form oluşturularak, oluşturulan formun Controller tarafında bir action metoda post edilmesi gerekmektedir.

@model MvcModelApp.Models.Person 

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Kişi Bİlgileri</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.FirstName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

Controller tarafında ise DefaultModelBinder tarafından doldurulmuş person nesnesinin kullanılması gerekmektedir.

      [HttpPost]
        public ActionResult CreatePerson(Person person)
        {
            // person nesnesi form bilgileri ile doldurulmuş durumda
            // person nesnesi artık gerekli işlem için 
            // (database, xml kaydı v.s) kullanılabilir

            return RedirectToAction("Index", "Home");
        }

Yukarıdaki action metodunun parametresi olan Person nesnesi DefaultModelBiner tarafından doldurulmuştur.  @model MvcModelApp.Models.Person ile view sayfasının modelinin belirlendiğini görüyoruz. Controller tarafında CreatePerson metoduna post edilen form bilgileri Person nesnesine doldurulmaktadır.

Bu işlem şöyle gerçekleşmektedir: DefaultModelBinder action metodun aldığı parametre tipinden (Person) bir nesne oluşturarak Property’lerini view sayfasından gelen form bilgileri ile eşleştirerek doldurmaktadır.

Umarım faydalı bir yazı olmuştur. Herkese iyi çalışmalar. Bir sonraki yazıda görüşmek üzere.

Global.asax Applicarion_End, Applicarion_Start, Session_End, Session_Start

22 Ağu

Global.asx dosyası içinde oturum açıldığı ve kapandığı sırada ya da uygulama ayağa kalktığında veya sonlandığında gerçekleşen ve açıklanmayı bekleyen bazı olaylar vardır. Bunlardan birkaçını açıklamaya çalışalım.

protected void Application_Start()

{

System.Diagnostics.Debug.WriteLine(“Application_started”);

 

AreaRegistration.RegisterAllAreas();

 

RegisterRoutes(RouteTable.Routes);

}

 

protected void Session_End(object sender, EventArgs e)

{

System.Diagnostics.Debug.WriteLine(“Session_End”);

}

 

protected void Session_Start(object sender, EventArgs e)

{

System.Diagnostics.Debug.WriteLine(“Session_Start”);

}

 

protected void Application_End()

{

System.Diagnostics.Debug.WriteLine(“Application_ended”);

}

 

Application_Start() metodu uygulama ilk çalıştırıldığında tetiklenecektir. Yani uygulamanın ApplicationDomain’i yaratıldığında oluşturulur. Her .Net Process’i default olarak bir tane Application domain’i içerir; bu application domain CLR tarafından işlemin başlatılması ile otomatik olarak yaratılır.

Application_End() metodu uygulama son bulduğunda tetiklenecektir.         Yani uygulamamız ApplicationDomain’den çıkarıldığında çalışacaktır.

Session_Start() metodu uygulamamıza bir istek geldiğinde tetiklenecektir. Daha basit bir anlatımla kullanıcı siteye girdiğinde çalışacaktır.

Session_End() metodunu kullanıcı hareketleriyle yakalamak imkansızdır. Yani kullanıcı siteye girdi ve browser’ı kapattı. Browser kapandığında Session_End metodu çalışmaz. Çünkü browser kapanması sunucuya bir istek göndermeyecek. Dolayısıyla istek olmadığı için cevapta olmayacaktır. Bir session server tarafında varsayılan olarak 20 dakika tutulur. Kullanıcı 20 dakika boyunca hiçbir işlem yapmazssa session otomatik olarak sonlandırılır ve Session_End() metodu çalışır.

Asp.net app_offline.htm dosyası Site Bakımda uyarısı

7 Ağu

Asp.net 2.0 ile birlikte gelen yeniliklerden bir taneside sunucuda çalışan uygulamalarımızı offline hale getirebileceğimiz özellik.

Web uygulamamızın kök dizinine app_offline.htm adında bir dosya bıraktığımızda Asp.net uygulamamızı kapatacaktır.

Artık uygulamamızın hiçbir sayfasına erişmek mümkün değildir. Sitemize gelen istekler direk app_offline.htm sayfasına yönlendirilecektir. app_offline.htm sayfasına sitenin kapalı olmasının sebebini bildirecek mesajlar verebiliriz. “Sitemiz geçici olarak bakıma alınmıştır.” gibi.

Bu yöntem uygulammızda köklü değişiklikler yaparken veya uygulamamızın çalışmasını etkileyecek küçük değişiklikler yaparken faydalı olacaktır.

Yapılan değişiklikler bittikten sonra app_offline.htm dosyasını kaldırdığımızda Asp.net uygulamamızı tekrar başlatacak ve uygulamamız yeni haliyle kaldığı yerden yoluna devam edecektir.