ASP.NET Web API'sinde İçerik Anlaşması

Bu makalede, ASP.NET Web API'lerinin ASP.NET 4.x için içerik anlaşmasını nasıl uyguladığı açıklanmaktadır.

HTTP belirtimi (RFC 2616), içerik anlaşmasını "birden çok gösterim olduğunda belirli bir yanıt için en iyi gösterimi seçme işlemi" olarak tanımlar. HTTP'de içerik anlaşması için birincil mekanizma şu istek üst bilgileridir:

  • Kabul: Yanıt için "application/json", "application/xml" veya "application/vnd.example+xml" gibi özel medya türleri kabul edilebilir
  • Accept-Charset: UTF-8 veya ISO 8859-1 gibi hangi karakter kümeleri kabul edilebilir?
  • Kodlamayı Kabul Et: Gzip gibi hangi içerik kodlamalarının kabul edilebilir olduğu.
  • Kabul Dili: "En-us" gibi tercih edilen doğal dil.

Sunucu, HTTP isteğinin diğer bölümlerine de bakabilir. Örneğin, istek bir AJAX isteğini gösteren bir X-Requested-With üst bilgisi içeriyorsa, Accept üst bilgisi yoksa sunucu varsayılan olarak JSON olabilir.

Bu makalede, Web API'sinin Accept ve Accept-Charset üst bilgilerini nasıl kullandığına bakacağız. (Şu anda Accept-Encoding veya Accept-Language için yerleşik destek yoktur.)

Serileştirme

Bir Web API denetleyicisi clr türü olarak bir kaynak döndürürse, işlem hattı dönüş değerini seri hale getirir ve HTTP yanıt gövdesine yazar.

Örneğin, aşağıdaki denetleyici eylemini göz önünde bulundurun:

public Product GetProduct(int id)
{
    var item = _products.FirstOrDefault(p => p.ID == id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return item; 
}

bir istemci şu HTTP isteğini gönderebilir:

GET http://localhost.:21069/api/products/1 HTTP/1.1
Host: localhost.:21069
Accept: application/json, text/javascript, */*; q=0.01

Yanıt olarak, sunucu şunları gönderebilir:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 57
Connection: Close

{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

Bu örnekte istemci JSON, Javascript veya "herhangi bir şey" (*/*) istedi. Sunucu nesnenin Product JSON gösterimiyle yanıt verdi. Yanıttaki Content-Type üst bilgisinin "application/json" olarak ayarlandığına dikkat edin.

Denetleyici bir HttpResponseMessage nesnesi de döndürebilir. Yanıt gövdesi için bir CLR nesnesi belirtmek için CreateResponse uzantısı yöntemini çağırın:

public HttpResponseMessage GetProduct(int id)
{
    var item = _products.FirstOrDefault(p => p.ID == id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return Request.CreateResponse(HttpStatusCode.OK, item);
}

Bu seçenek, yanıtın ayrıntıları üzerinde daha fazla denetim sağlar. Durum kodunu ayarlayabilir, HTTP üst bilgileri ekleyebilir ve benzeri işlemleri yapabilirsiniz.

Kaynağı seri hale getiren nesne, medya biçimlendirici olarak adlandırılır. Medya biçimlendiricileri MediaTypeFormatter sınıfından türetilir. Web API'sinde XML ve JSON için medya biçimlendiricileri sağlanır ve diğer medya türlerini desteklemek için özel biçimlendiriciler oluşturabilirsiniz. Özel biçimlendirici yazma hakkında bilgi için bkz. Medya Biçimlendiricileri.

İçerik Anlaşması Nasıl Çalışır?

İlk olarak işlem hattı, HttpConfiguration nesnesinden IContentNegotiator hizmetini alır. Ayrıca HttpConfiguration.Formatters koleksiyonundan medya biçimlendiricilerinin listesini alır.

Ardından işlem hattı IContentNegotiator.Negotiate çağrısı yaparak şunları geçirir:

  • Serileştirecek nesnenin türü
  • Medya biçimlendiricileri koleksiyonu
  • HTTP isteği

Negotiate yöntemi iki bilgi parçası döndürür:

  • Kullanılacak biçimlendirici
  • Yanıtın medya türü

Biçimlendirici bulunmazsa Negotiate yöntemi null döndürür ve istemci 406 (Kabul Edilemez) HTTP hatası alır.

Aşağıdaki kod, bir denetleyicinin doğrudan içerik anlaşmasını nasıl çağırabileceğini gösterir:

public HttpResponseMessage GetProduct(int id)
{
    var product = new Product() 
        { Id = id, Name = "Gizmo", Category = "Widgets", Price = 1.99M };

    IContentNegotiator negotiator = this.Configuration.Services.GetContentNegotiator();

    ContentNegotiationResult result = negotiator.Negotiate(
        typeof(Product), this.Request, this.Configuration.Formatters);
    if (result == null)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotAcceptable);
        throw new HttpResponseException(response));
    }

    return new HttpResponseMessage()
    {
        Content = new ObjectContent<Product>(
            product,		        // What we are serializing 
            result.Formatter,           // The media formatter
            result.MediaType.MediaType  // The MIME type
        )
    };
}

Bu kod, işlem hattının otomatik olarak yaptığı işlemle eşdeğerdir.

Varsayılan İçerik Anlaşmalı

DefaultContentNegotiator sınıfı, IContentNegotiator'ın varsayılan uygulamasını sağlar. Biçimlendirici seçmek için çeşitli ölçütler kullanır.

İlk olarak, biçimlendiricinin türü seri hale getirebilmesi gerekir. Bu, MediaTypeFormatter.CanWriteType çağrılarak doğrulanır.

Ardından, içerik müzakerecisi her biçimlendiriciye bakar ve HTTP isteğiyle ne kadar iyi eşleşir değerlendirmesini sağlar. Eşleşmeyi değerlendirmek için içerik müzakerecisi biçimlendiricide iki şeye bakar:

  • Desteklenen medya türlerinin listesini içeren SupportedMediaTypes koleksiyonu. İçerik müzakerecisi bu listeyi Accept üst bilgisi isteğiyle eşleştirmeye çalışır. Accept üst bilgisinin aralıklar içerebileceğini unutmayın. Örneğin, "metin/düz" metin/* veya */* ile eşleşir.
  • MediaTypeMapping nesnelerinin listesini içeren MediaTypeMappings koleksiyonu. MediaTypeMapping sınıfı, HTTP isteklerini medya türleriyle eşleştirmek için genel bir yol sağlar. Örneğin, özel bir HTTP üst bilgisini belirli bir medya türüyle eşler.

Birden çok eşleşme varsa, en yüksek kalite faktörüne sahip eşleşme kazanır. Örnek:

Accept: application/json, application/xml; q=0.9, */*; q=0.1

Bu örnekte, application/json 1.0 zımni bir kalite faktörüne sahiptir, bu nedenle uygulama/xml yerine tercih edilir.

Eşleşme bulunmazsa, içerik müzakerecisi varsa istek gövdesinin medya türüyle eşleşmeyi dener. Örneğin, istek JSON verileri içeriyorsa, içerik müzakerecisi bir JSON biçimlendirici arar.

Hala eşleşme yoksa, içerik müzakerecisi yalnızca türü seri hale getirebilecek ilk biçimlendiriciyi seçer.

Karakter Kodlaması Seçme

Bir biçimlendirici seçildikten sonra, içerik müzakerecisi biçimlendiricideki SupportedEncodings özelliğine bakarak ve bunu istekteki Accept-Charset üst bilgiyle eşleştirerek (varsa) en iyi karakter kodlamasını seçer.