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 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 Web Api ve Content Negotiation Kavramı

20 Mar

Günümüzde artık internet dünyası sadece kişisel bilgisayarlarda internet tarayıcıları tarafından görüntülenebilen web sitelerinden oluşmuyor. Mobil cihazlar, Televizyonlar, Akıllı Evler Aletleri v.b gibi birçok cihaz artık internet aleminde kaynak tüketimine dahil olmuş durumdadır. Şüphesiz bu giderek daha da genişleyecek. Bu çeşitlilik karşısında artık internet dünyasına sunulan bilgilerin nasıl temsil edildiği öne çıkmıştır. Örneğin bir cihaz JSON veri tipiyle işlem yaparken diğeri XML veri tipini tercih ediyor olabilir. HTTP üzerinden kaynak tüketimi göz önüne alındığında verilerin temsili(representation) content negotaition kavramı ile bütünleşiyor.

Bir önceki yazıda HTTP protokolüne özel bir kavram olan Content Negotiation kavramından genel olarak bahsetmiştik. Bu yazıda ise content negotiation konusunu Asp.Net Web api tarafından incelemeye çalışacağız.

Asp.net Web Api teknolojisi Content Negotiation ile uyumlu çalışmaktadır. Ancak bu kavram Web Api teknolojisinin bir parçası gibi anlaşılmamalıdır. Content Negotation kavramı W3C(World Wide Web Consortium) tarafından tanımlanmış bir internet içerik standartıdır. RFC(Request For Comment) döküman numarası 2616‘dır.

Content Negotiation kavramı Asp.net Web Api tarafında, kullanıcıların talebine göre verinin hangi biçimde sunulacağını belirlemek için kullanılan sunucu odaklı bir çözüm yöntemi olarak kullanılmaktadır. Kullanıcılar talep ettikleri ortam döküman türlerini HTTP header bilgisi olarak belirleyebilirler. Talepteki Header bilgisi Web Api tarafından değerlendirilerek uygun şekilde bir cevap oluşturulur ve HTTP protokolü üzerinden kullanıcıya sunulur.

Web Api bir kullanıcının hangi niyetle kaynak talep ettiğini anlamak için talebin(HttpRequest) Header bilgilerini kontrol eder. Header bilgilerinde içerik ile ilgili kısımlar şu şekilde sıralanabilir:

  • Content-Type: API Burada belirtilen formatta veri sunar.
  • Accept: Kullanıcı burada kabul ettiği veri türlerini belirtir. (application/json gibi)
  • Accept-Charset:Kabul edilen karakter kodlaması. (UTF-8 gibi)
  • Accept-Language: Kabul edilen dil seçeneği. (en-us, tr-tr gibi)
  • Accept-Encoding:Kabul edilen içerik kodlaması. (gzip gibi)

Web Api uygulamasına Fiddlr gibi bir araç yardımıyla sorgulamalar yaparak durumu inceleyebiliriz.

Örneğin http://localhost:15063/api/values için bir talep gönderdiğimizde,

fidd1
Header bildirimi yok

Web Api için oluşturulan talepte içerik bilgisine ait bir bildirim yapılmadı. Ancak sonucun JSON formatında geldiğini gözlemliyoruz. Bunun sebebi Web Api uygulamasının varsayılan olarak JSON formatında veri oluşturmasıdır.

Şimdi içerik talebini JSON değilde XML şeklinde istemeyi deneyelim. Bu durumda Content-Type: application/xml şeklinde bir Header bildirimi yapmamız gerekmektedir.

Content-Tyle:application/xml
Content-Tyle:application/xml

Dikat edecek olursak aynı URI üzerinden aynı kaynağa eriştiğimiz halde dönen cevap XML formatı şeklinde oldu. Yani kaynağın temsil ediliş şeklini HTTP Content Negotiation ile değiştirmiş olduk.

Farklı formatlarda veri alma talebini bir MVC uygulaması veya Web Froms ile yapmış olsaydık ya farklı URI üzerinden (sayfa veya action metod) oluşturacaktık, ya da aynı URI adresine parametre olarak kaynak türünü xml şeklinde belirtmiş olmamız gerekebilirdi.

Kullanıcılar Web Api uygulamalarına hangi türde veri kabul ettiklerini Accept header bilgisi ile bildirirler. Örneğin üç farklı formatta veri kabul ettiğimizi bildirecek olursak: Accept: application/xml, application/json, text/javascript

Accept Header
Accept Header

Bu durumda varsayılan değer olan JSON formatı şeklinde veri döner. application/json seçeneğini kaldırırsak bu sefer de XML veri döner.

Web Api tarafında medya tiplerine göre uygun içerik üretmek için Media Type Formatter sınıfları oluşturulur ve akışa dahil edilir. Eğer JSON veya XML gibi türler dışında kendi özel türümüzü oluşturmak istiyorsak System.Net.Http.Formatting.MediaTypeFormatter sınıfından türeteceğimiz sınıflarda istediğimiz veri formatlarını oluşturabiliriz.

 

Asp.Net Web Api Message Handler Yapısı

21 Şub

Asp.Net Web Api 2 çatısı bünyesinde, Message Handler tipleri HTTP talebini alarak geriye yine HTTP cevap döndüren somut sınıflardır. Message Handler tipleri Web Api iletim hattında(pipeline) adeta bir zincir misali dizilmiş şekilde bulunur. Bu zincirdeki ilk handler talebi alır yapması gereken işlemi bitirdikten sonra talebi bir sonraki handler sınıfına aktarır. Bazı handler sınıfları talebi bir sonraki handler sınıfına aktarmaya gerek duymadan akışı kesip cevabı kullanıcıya döndürebilir. Bu işleyiş şekli Web Api tarafında delegating handler olarak adlandırılır. Asp.Net Web Api 2 versiyonuna ait mesaj yaşam döngüsüne ait şema buradaki posterde sunulmuştur.

Web Api iletim akışına dahil olan mevcut message handler tiplerine örnek verecek olursak:

  • HttpServer, talepleri alır.
  • HttpRoutingDispatcher, route kuralına göre talebi ilgili kaynağa yönlendirir.
  • HttpControllerDispatcher, talebi ilgili controller sınıfına gönderir.

Kendi oluşturduğumuz bir handler tipini akışa dahil etmemiz mümkündür. Örneğin, gelen HTTP talebi eğer PDF tipinde bir dosya istiyorsa cevap olarak yetkisiz kullanıcı şeklinde bir mesaj döndürebiliriz.

Server Handler

Server Handler

Resimde ifade edildiği üzere MessageHandler1 ve MessageHandler2 tipleri geliştiriciler tarafından özel olarak oluşturularak akışa dahil edilmiştir.

Kendi özel message handler tiplerimizi oluştururken, System.Net.Http.DelegatingHandler soyut(abstract) sınıfından bir sınıf türetmeliyiz. Daha sonra bu sınıfın SendAsync() metodunu override ederek talebi değerlendirebiliriz.

public class MessageHandler1 : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(
          HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Talebin kontrol edildiği alan.

        var response = await base.SendAsync(request, cancellationToken);

        // Cevabın kontrol edildiği alan. (await, base.sendAsync() işlemi bitene kadar süreci bekletir)

        return response;
    }
}

SendAsync() metodu HTTP talebini  HttpRequestMessage tipinde alır ve geriye HttpResponseMessage tipinde asenkron olarak bir mesaj döndürür. base.SendAsync() metosu ise  mesajı Web Api akışındaki mevcut handler tipine aktarır. Bu durumda kullanıcıya dönen mesaj, dahili Web Api handler tipi tarafından sunulur.

Eğer Web Api akışındaki handler tiplerine işi bırakmadan kullanıcıya mesajı kendimiz vermek istiyorsak aşağıdaki gibi bir yöntem izlememiz gerekmektedir.

public class MessageHandler2 : DelegatingHandler
{
      protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
     {
        // HttpResponseMessage tipinde bir cevap oluşturulur.
        var response = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent("Hello!")
        };

        // Not: TaskCompletionSource sınıfı bir Task oluşturur ve sarmalar ancak delege almaz.
        var tsc = new TaskCompletionSource<HttpResponseMessage>();
        tsc.SetResult(response);
        return tsc.Task;
     }
}

webapi_handlers_04

Bu kodda dikkat edersek base.SendAsync() metodunu kullanmadık. HttpResponseMessage tipinde bir cevabı kendimiz oluşturarak ve kullanıcıya sunmuş olduk.

Oluşturduğumuz message handler tiplerini akışa dahil etmek için HttpConfiguration sınıfının MessageHandlers kolleksiyonuna eklememiz gerekmektedir. Asp.Net MVC 4 web uygulamasında bu işlem şu şekilde yapılabilir:

public static class WebApiConfig
{
       public static void Register(HttpConfiguration config)
       {
          config.MessageHandlers.Add(new MessageHandler1());
          config.MessageHandlers.Add(new MessageHandler2());
       }
}

Message handler eklemeleri yapılırken sıralama önemlidir. Çünkü akışta çalışma sıralaması bu şeklide olacaktır. Çalışma sıralamasını görmek için handler sınıfları içine Debug.WriteLine() medodu ile mesajlar yazdırıp debug modunda sıralamayı görebiliriz.

Eğer Web Api uygulaması self hosting yöntemiyle barındırılıyorsa konfigurasyon ayarlaması şu şekilde olacaktır:

var config = new HttpSelfHostConfiguration("http://localhost");
config.MessageHandlers.Add(new MessageHandler1());
config.MessageHandlers.Add(new MessageHandler2());

Bu şekilde eklenen message handler tipleri global olarak eklenmektedir ve sunucuya gelen her talebi etkiler. Ancak bu istenmeyen bir durum olabilir. Bir diğer yöntem ise talep edilen kaynağa göre Route ayarlaması sırasında message handler tiplerinin akışa dahil edilmesidir.

public static class WebApiConfig
{
     public static void Register(HttpConfiguration config)
     {
         config.Routes.MapHttpRoute(
             name: "Route1",
             routeTemplate: "api/{controller}/{id}",
             defaults: new { id = RouteParameter.Optional }
         );

         config.Routes.MapHttpRoute(
             name: "Route2",
             routeTemplate: "api/{controller}/{id}",
             defaults: new { id = RouteParameter.Optional },
             constraints: null,
             handler: new MessageHandler2() // per-route message handler
         );

         config.MessageHandlers.Add(new MessageHandler1());  // global message handler
     }
}

Bu örneğe göre eğer gelen talebe ait URI, Route2 ile eşleşirse talep MessageHandler2  tarafına yönlendirilir. Aşağıdaki diyagramda akış yönlendirmesi grafiksel olarak gösterilmiştir.

webapi_handlers_05

 

Talep önce global olarak tanımlanan MessageHandler1 üzerinden geçip ardından HttpRouteDispatcher tarafından belirlenen kurala göre yönlendirilmektedir. Burada dikkat edecek olursak MessageHandler2 kullanıldığında Web Api yapısındaki mevcut HttpControllerDispatcher tipi devre dışı kalmıştır. Bu nedenle Route2 ile eşleşen talepler hiçbir controller sınıfına uğramazlar. Bu sayede kendi özel tiplerimizle controller görevi gören mekanizmalar oluşturabiliriz.

Eğer route mekanizmasından geçen talebin tekrar bir HttpControllerDispatcher üzerinden geçmesini istiyorsak şu şekilde bir ayarlama yapmamız gerekir:

// List of delegating handlers.
DelegatingHandler[] handlers = new DelegatingHandler[] {
      new MessageHandler3()
};

// Create a message handler chain with an end-point.
var routeHandlers = HttpClientFactory.CreatePipeline(
                  new HttpControllerDispatcher(config), handlers);

config.Routes.MapHttpRoute(
       name: "Route2",
       routeTemplate: "api2/{controller}/{id}",
       defaults: new { id = RouteParameter.Optional },
       constraints: null,
       handler: routeHandlers
);

Bu yapıya ilişkin diyagram aşağıdaki gibidir.

webapi_handlers_06

 

Kaynak: http://www.asp.net/web-api/overview/advanced/http-message-handlers

Asp.Net Web Api ile Kullanıcı Doğrulama ve Yetkilendirme

15 Şub

Asp.Net Web Api ile oluşturulan uygulamalar dış dünyaya tamamen açık olmayacaksa yani bazı kaynaklar sadece kayıtlı kullanıcılara sunulacaksa bu noktada Api güvenliğinin sağlanması gerekmektedir. Api güvenliği temelde, sistem kullanıcıların kim olduğunu ve hangi kaynaklara erişebileceğini belirleyen bir mekanizmanın oluşturulması ile sağlanabilir.

Güvenlik mekanizmalarının oluşturulmasında kullanılan iki temel kavram vardır. Bunlar:

  • Kullanıcı Doğrulama (Authentication), bir kullanıcının kim olduğunu belirleme işlemidir.
  • Kullanıcı Yetkilendirme (Authorization), belirlenen kullanıcıların hangi kaynaklara erişeceğini belirleme işlemidir.

Kullanıcı Doğrulama (Authentication)

Web Api çatısı, kullanıcı doğrulama işleminin host tarafından gerçekleştiğini varsayar. Örneğin IIS üzerinde host edilmiş bir uygulama, ilgili HTTP modülü tarafından doğrulanır. Kendi yazdığımız bir HTTP modülü üzerinde de doğrulama işlemini gerçekleştirebiliriz. Ancak alternatif olarak doğrulama işlemi Web Api HTTP Message Handler tarafından da gerçekleştirilebilir. Bu yöntemde sorumluluk Web Api message handler tarafına yüklenmiş olur. Bu durum aşağıdaki sebeplerden dolayı geliştiricide bazı çekinceler meydana getirebilir:

  • HTTP modülleri, Asp.Net iletim hattı(pipeline) boyunca tüm talepleri kontrol eder, ancak Web Api message handler tipleri sadece Web Api üzerine yönlendirilen talepleri kontrol eder.
  • Geliştirici isterse Route mekanizmasına göre doğrulama yapabilir.
  • HTTP modülleri IIS için özeldir, message handler tipleri ise Web Api host ortamı için özeldir. Web host veya self host.
  • HTTP modülleri IIS logging ve denetim sürecine dahildir.
  • HTTP modülleri, iletim akışın başlamadan önce çalışır. Eğer message handler kullanırsanız handler çalışana kadar kimlik bilgileri belirlenmez.

Hangi yöntem kullanılmalı konusunda kısaca şunu söylemek gerekir ki: Eğer web host kullanılacaksa HTTP modül kullanmak iyi bir seçim olacaktır. Ancak self host kullanılacaksa Web Api message handler kullanmak uygun olacaktır.

Eğer kullanıcı doğrulama işlemini kendi özel sınıfınız yardımıyla gerçekleştirdiyseniz iki farklı noktaya kullanıcı bilgilerini kaydetmelisiniz. Bunlar:

  • Thread.CurrentPrincipal
  • HttpContext.Current.User
private void SetPrincipal(IPrincipal principal)
{
   Thread.CurrentPrincipal = principal;
   if (HttpContext.Current != null)
   {
       HttpContext.Current.User = principal;
   }
}

Self host durumunda Thread.CurrentPrincipal kullanılır, Web host durumunda ise HttpContext.Current.User kullanılır.

Kullanıcı Yetkilendirme (Authorization)

Yetkilendirme işlemi, iletim hattında(pipeline) doğrulama işleminden sonra gelir. Yetkilendirme sırasında önce kullanıcının doğrulanmış olup olmadığı kontrol edilir. Yetkilendirme işlemi Controller veya Action seviyesinde gerçekleştirilebilir. Akışta yetkilendirme filitreleri controller ve action metodlarından önce çalışır.

webapi_auth01

Web Api uygulamalarında yetkilendirme işlemi filtreler akışa dahil edilir. Varsayılan olarak AuthorizeAttribute filtresi vardır. Filtreler iletim hattında ki akışa 3 şekilde dahil edilebilir.

  • Global seviyede
  • Controller seviyesinde
  • Action metod seviyesinde

Filtre aşağıdaki şekilde global seviyede akışa dahil edilebilir.

public static void Register(HttpConfiguration config)
{
    config.Filters.Add(new AuthorizeAttribute());
}

Eğer filtreler global olarak akışa dahil edilirse tüm talepler doğrulama işlemi için kontrol edilir. Eğer doğrulanmamış ise kullanıcıya Http 401 hata kodu döndürülür.

Controller seviyesinde sınırlandırma yapmak için filtre, attribute olarak kullanılır.

[Authorize]
public class ValuesController : ApiController
{
     public HttpResponseMessage Get(int id) { ... }
     public HttpResponseMessage Post() { ... }
}

Action seviyesinde sınırlandırma yapmak için de aynı attribute kullanılır.


public class ValuesController : ApiController
{
     [Authorize]
     public HttpResponseMessage Get(int id) { ... }
}

Bir controller sınıfı Authorize attribute ile işaretlendiğinde içerisindeki tüm action metodları  sınırlı erişime dahil olur. Ancak bazı action metodları sınırlamadan çıkarmak için AllowAnonymous attribute kullanılabilir.

[Authorize]
public class ValuesController : ApiController
{
    [AllowAnonymous]
    public HttpResponseMessage Get(int id) { ... }
    public HttpResponseMessage Post() { ... }
}

Authorize attribute kullanırken kullanıcı adı ve rol adı belirlemek de mümkündür. Bu şekilde sadece ilgili rol veya kullanıcıya izim verilmiş olur. Örnek:

[Authorize(Users="Bayram")]
public class ValuesController : ApiController{}

[Authorize(Roles="Admin")]
public class ValuesController : ApiController{}

Özel Yetkilendirme

Kendi özel sınıflarımızla yetkilendirme işlemini gerçekleştirmek istiyorsak oluşturacağımız sınıfı aşağıdaki tiplerden birinden türetmek gerekir.

  • AuthorizeAttribute, geçerli kullanıcı ve rolüne göre dayalı doğrulama için kullanılır.
  • AuthorizationFilterAttribute, senkron bir yetkilendirme için kullanılır. Geçerli kullanıcı olmasına gerek yoktur.
  • IAuthorizationFilter, asenkron bir yetkilendirme için kullanılır. Örneğin dosya sistemi veya network kullanıdığı durumlarda.

webapi_auth02

Kaynak: http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api

 

Asp.Net Web API verileri ile Google Charts kullanımı

19 Tem

Farklı platformların birbiri ile konuşabilmesi biz yazılımcılar için gerçekten büyük bir nimet. Bunu ASP.NET Web API verilerini kullanarak Google Chart API grafiklerini kolay bir şekilde çizdirebildiğimde bir kez daha anlamış oldum.

Geliştiriciler için birçok kullanışlı ürün ortaya koyan Google Google Chart API uygulaması ile grafik kullanmak isteyenlere yeni bir kapı açmıştır. Platform bağımsız uygulamalar konusunda çok iyi ürünler ortaya koymaya devam etmektedir. Bende bu grafik ürününü incelerken hemen bir Web API oluşturup kullanmak istedim. Başarılı sonuca ulaşınca da bunu sizlerle paylaşmak istedim.

Örnek bir Web API uygulaması üzerinden Google Grafiklerini çizdirmeye çalışalım. Hemen boş bir ASP.NET MVC uygulaması oluşturarak işe başlayalım. Örnek uygulamamızda hedeflenen buradaki Google sütun grafiğini çizmektir. Uygulama çalıştığında ulaşacağımız grafik aşağıdaki gibi olacaktır.

ASP.NET WEB API ve Google Charts

Grafikte kullanılacak olan veri modelini aşağıdaki şekilde oluşturarak işe başlayalım.

public class CompanyReport
{
    public string Year { get; set; }
    public double Sales { get; set; }
    public double Expense { get; set; }
 }

Model sınıfımız yıllara göre satış ve giderleri veren bir özet niteliğinde olup grafikte olması gereken verileri tutmaktadır.

Şimdi de bu veri tipine uygun verileri servis edecek olan bir Api Controller oluşturmalıyız.

public class CompanyController : ApiController
{
    public IEnumerable Get()
    {
         var result = new List
         {
             new CompanyReport{Year = "2008&", Sales = 1000, Expense = 452},
             new CompanyReport{Year = "2009", Sales = 1150, Expense = 652},
             new CompanyReport{Year = "2010", Sales = 950, Expense = 1052},
             new CompanyReport{Year = "2011", Sales = 1150, Expense = 252},
         };

         return result;
     }
 }

Api Controller sınıfımız olan CompanyController sınıfının Get() metodu CompanyReport verilerini içeren JSON tipinde bir veri seti döndürmektedir. Google Chart API aynı şekilde grafik çizim verilerini JSON tipinde almaktadır. Bu sayede en başa dönecek olursak, iki ayrık sistemi JSON ortak noktasında buluşturmuş olacağız.

Grafik çizim işlemini View tarafında bir Script içerisinde gerçekleştirmeye çalışalım. Scrip içerisine referans olan Google Char ve JQuery Javascript dosyalarını Google CDN üzerinden çağırmaktayım.

<script src="https://www.google.com/jsapi" type="text/javascript">// <![CDATA[
  <script type='text/javascript'
          src='https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js'/>
  <script type="text/javascript">

        function drawChart() {
           $.getJSON(&quot;/api/Company&quot;, null, function (items) {
               var table = new google.visualization.DataTable();
               table.addColumn('string', 'Yıllar');
               table.addColumn('number', 'Satışlar');
               table.addColumn('number', 'Giderler');

               for (var i = 0; i < items.length; i++) {
                     table.addRow([items[i].Year, items[i].Sales,items[i].Expense]);
               }

               var options = { title: 'Şirket Raporları' };
               var chart = new google.visualization.ColumnChart(
                                document.getElementById('chart_div'));
               chart.draw(table, options);
           });
        }
  google.load(&quot;visualization&quot;, &quot;1&quot;, { packages: [&quot;corechart&quot;] });
  google.setOnLoadCallback(drawChart);
// ]]></script>

Artık uygulamamızı çalıştırdığımızda Api verileriyle Google grafikleri çizilmiş olacaktır.

Bir sonraki yazıda görüşmek dileğiyle.

Kaynak Kod: Burada

Asp.Net MVC Web API Projesinde Ninject ile Dependency Injection

5 Tem

Bir önceki yazılarımdan biri olan MVC Projesinde Dependency Injection başlıklı yazıda MVC uygulamasında Controller sınıfı içerisine dışarıdan bağımlılıkların enjekte edilmesini incelemeye çalışmıştık. Bu yazımızda ise Asp.Net MVC Web Api uygulaması üzerinde Dependency Injection işlemini uygulamaya çalışacağız.

MVC uygulamasında Controller sınıfları System.Web.Mvc.Controller tipinden türetilirken API Controller sınıfları System.Web.Http.ApiController tipinden türetilmiştir. Bu nedenle Web API üzerinde Dependency Injection uygularken bir önceki örnek uygulamadaki yöntemden daha farklı bir yöntem kullanacağız.

public class ValuesController : ApiController
{
     readonly IProductRepository productRepository;
     public ValuesController(IProductRepository productRepository)
     {
          this.productRepository = productRepository;
     }

     public IEnumerable<string> Get()
     {
         return productRepository.GetValues();
     }
}

API içerisine bağımlılıkları enjekte ederken IProductRepository interface tipini uygulayan(implemente eden) sınıfları göndermeye dikkat edeceğiz.

WEB API içerisinde Dependency Injection işlemini gerçekleştirmek için yine MVC’de olduğu gibi IoC container kütüphanelerinden faydalanmamız gerekiyor.

WEB API projelerinde Controller sınıflarının parametreli şekilde çalışabilmesi için bazı ayarlamaları yapmamız gerekecek. Bu ayarlamalardan biri, System.Web.Http alanında bulunan GlobalConfiguration statik sınıfındaki DependencyResolver özelliğinin düzenlenmesi olacaktır. Bu sayede WEB API, resolver mekanizmasını tanıyacaktır. Bu ayarlama Global.asax.cs dosyası içerisinde yapılmaktadır. Tanımlama:

public class WebApiApplication : HttpApplication
{
     protected void Application_Start()
     {
         // Diğer kodlar...
         GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(CreateKernel());
     }

     private IKernel CreateKernel()
     {
         IKernel kernel = new StandardKernel();

         kernel.Bind<IProductRepository>().To<ProductRepository>();

         return kernel;
     }

}

NinjectDependencyResolver adında bir sınıf hazırlıyoruz ve yukarıdaki gibi DependencyResolver özelliğine atıyoruz. DependencyResolver özelliği IDependencyResolver tipindedir. Bu sebeple NinjectDependencyResolver sınıfı da IDependencyReolver tipini uygulamalıdır.

public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
      private readonly IKernel kernel;

      public NinjectDependencyResolver(IKernel kernel): base(kernel)
      {
          this.kernel = kernel;
      }

      public IDependencyScope BeginScope()
      {
          return new NinjectDependencyScope(kernel.BeginBlock());
      }
}

NinjectDependencyResolver sınıfını aynı zamanda NinjectDependencyScope sınıfından türetiyoruz. Bunun sebebi ise IDependencyResolver interface tipinin barındırdığı BeginScope() metodudur. Bu metodun bir şekilde doldurulması gerekmektedir. NinjectDependencyScope tipi de kendi oluşturmuş olduğumuz bir sınıf olacaktır. Bu sınıfı de şu şekilde oluşturabiliriz.

public class NinjectDependencyScope : IDependencyScope
{
    IResolutionRoot resolver;

    public NinjectDependencyScope( IResolutionRoot resolver)
    {
        this.resolver = resolver;
    }

     public void Dispose()
     {
        IDisposable disposable = resolver as IDisposable;

        if (disposable!=null)
        {
            disposable.Dispose();
        }

        resolver = null;
     }

     public object GetService(Type serviceType)
     {
         if (resolver==null)
         {
             throw new ObjectDisposedException("this", "This scope has been disposed");
         }

         return resolver.TryGet(serviceType);
     }

     public IEnumerable<object> GetServices(Type serviceType)
     {
         if (resolver == null)
         {

              throw new ObjectDisposedException("this", "This scope has been disposed");
         }

         return resolver.GetAll(serviceType);
     }

}

Son aşamayı da bu şekilde tamamladıktan sonra projemiz çalışabilir hale gelmiş demektir.

WEB API Sonuç
WEB API Sonuç

Projenin örnek kodlarını SkyDrive üzerinden paylaşıyorum.

Kaynak Kod: Burada