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
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;
}
}
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.
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.
Kaynak: http://www.asp.net/web-api/overview/advanced/http-message-handlers