Categories: Asp.Net Web API

Asp.Net Web API’da Accept-Language ile Localization

Merhaba arkadaşlar.

Yine bir Web API makalesi ile buradayım. Bir süredir ocak ayı içerisinde çıkacak olan Web API kitabi üzerinde yoğun olarak çalışmaktayız. Bu süre zarfında bloğumuda boş bırakmamak adına, Web API içerisinde Localization işlemleri adına bir makale yayınlamak istedim. Haydi bir bakalım nasıl oluyormuş?

Asp.NET Web API’da client’ın Accept-Language header bilgisini kullanarak, server’a göndermiş olduğu language parametrelerini Asp.Net Web API içerisinde nasıl handle edebileceğimizi bir örnek üzerinden inceleyeceğiz.

Öncelikle örneğimizde kullanacağımız Customer model’ini tanımlayalım:

namespace WebAPILocalization.Models
{
    public class Customer
    {
        public string Name { get; set; }
        public string Email { get; set; }
    }
}

Customer model’ini oluşturduktan sonra, örneğimiz için olan statik dil verilerini tutabilmek için solution üzerine App_GlobalResources isimli Asp.Net Folder’ını ekleyeceğiz ve içerisine iki adet “WebApiResource.resx” ve “WebAPIResource.en-US.resx” isimli resource item’larını tanımlıyor olacağız. İlk olarak bizim için default resource bilgisi olarak kabul edeceğimiz Türkçe içeriklerin yer alacağı “WebAPIResource.resx” isimli resource dosyasını ekleyelim ve içeriğini aşağıdaki şekildeki gibi girelim:

Şimdi İngilizce içeriklerin yer alacağı “WebAPIResource.en-US.resx” isimli resource dosyasını ekleyelim ve bununda içeriğini aşağıdaki gibi girelim:

Statik dilleri tuttuğumuz resource dosyalarını oluşturduktan sonra aşağıdaki gibi bir solution yapısı olacaktır.

Resource’ları tamamladıktan sonra şimdi client üzerinden gelecek olan Accept-Language header bilgisini alıp, API tarafında o dil bilgisi destekleniyor ise, culture bilgisini o dil bilgisine göre güncelleyeceğiz. Bu sayede statik resource’lar güncellenen culture bilgisine göre geriye değer döneceklerdir.

Culture bilgisini handle edebilmek için ise yeni bir MessageHandler oluşturacağız. Oluşturacak olduğumuz handler’ı System.Net.Http.DelegatingHandler’dan türeteceğiz ve SendAsync method’unu override ederek, request üzerinde gönderilen Accept-Language bilgilerine ulaşacağız. Dilerseniz şimdi LocalizationMessageHandler isminde bir handler oluşturalım:

using System.Collections.Generic;
using System.Net.Http;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;

namespace WebAPILocalization.Handlers
{
    public class LocalizationMessageHandler : DelegatingHandler
    {
        private readonly List<string> _supportedLanguages = new List<string>() { "tr-TR", "en-US" };

        protected override async Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            SetCulture(request);

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

        #region Private Methods
        private void SetCulture(HttpRequestMessage request)
        {
            foreach (var loopLanguage in request.Headers.AcceptLanguage)
            {
                // Desteklediğimiz dillerden biri var ise culture bilgisini o dile göre güncelliyoruz.
                if (_supportedLanguages.Contains(loopLanguage.Value))
                {
                    Thread.CurrentThread.CurrentCulture = new CultureInfo(loopLanguage.Value);
                    Thread.CurrentThread.CurrentUICulture = new CultureInfo(loopLanguage.Value);

                    break;
                }
            }
        }
        #endregion
    }
}

Kod bloğunda gördüğümüz gibi SendAsync method’unu override edip, içerisinden private olarak oluşturmuş olduğumuz SetCulture method’unu çağırıyoruz ve tekrardan base’deki SendAsync method’unu çağırarak request’i inner handler’a aktarıyoruz . SetCulture method’u içerisinde ise request’in headers kısmında gönderilen Accept Language listesi üzerinde bir iterate işlemi yaparak, sırasıyla gelen dil bilgisini private olarak yukarıda tanımlamış olduğumuz supportedLanguages” listesi üzerinden kontrol ederek, eğer gelen dil bilgisi “supportedLanguages” listesi içerisinde var ise, culture bilgisini bu dil bilgisine göre güncelliyoruz ve iterate işlemine son veriyoruz.

Bir önceki bölüm olan “Accept-Language Header’ı Anlamak” kısmında bahsettiğimiz gibi client birden fazla dil bilgisi parametresi gönderebilmekteydi. Burada yapmış olduğumuz işlem ise, client’ın istediği ilk dil bilgisine göre geriye response dönebilmektir.

Şimdi oluşturmuş olduğumuz LocalizationMessageHandler’ın pipeline içerisinde devreye girebilmesi için WebApiConfig içerisinde MessageHandlers listesine eklememiz gerekmektedir.

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

LocalizationMessageHandler’ı pipeline’a register ettikten sonra, örneğimizi deneyebilmek için CustomerController’ı aşağıdaki gibi oluşturalım:

public class CustomerController : ApiController
{
    public Customer Get()
    {
        return new Customer()
        {
            Name = Resources.WebAPIResource.NameField,
            Email = Resources.WebAPIResource.EmailField
        };
    }
}

Controller’ıda hazırladık ve şimdi Fiddler üzerinden test işlemine başlayabiliriz. Fiddler üzerinden göndereceğimiz Accept-Language header bilgisine göre controller üzerinden Customer objesinin Name ve Email alanlarının istenilen dil bazında resource üzerinden gelmesini beklemekteyiz.

Fiddler üzerinden API’nin Get method’unu Accept-Language header bilgisini “tr-TR” şeklinde girerek aşağıdaki gibi çağıracağız:

API’nin Get method’unu Fiddler üzerinden execute etmeden önce LocalizationMessageHandler’ın SetCulture method’una breakpoint koyup sonrasında ise execute ettiğimizde henüz request, controller’a iletmeden önce LocalizationMessageHandler’ın SetCulture method’unun devreye girdiğini ve AcceptLanguage property’sininde “tr-TR” olarak set edildiğini aşağıdaki ekranda görebiliriz.

Request, LocalizationMessageHandler’dan çıktıktan sonra CustomerController’a gelip, Get method’una düşmektedir.  LocalizationMessageHandler içerisinde kullanıcının göndermiş olduğu Accept-Language bilgisine göre Thread üzerindeki Culture bilgisini güncellediğimiz için, CustomerController’ın Get method’u içerisindeki Resource’lar değerlerini getirirken, istenilen culture bilgisine göre getirmektedirler ve aşağıdaki gibi bir response oluşmaktadır:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?YzpcdXNlcnNcZ8O2a2FscFxkb2N1bWVudHNcdmlzdWFsIHN0dWRpbyAyMDE1XFByb2plY3RzXFdlYkFQSUxvY2FsaXphdGlvblxXZWJBUElMb2NhbGl6YXRpb25cYXBpXGN1c3RvbWVyXA==?=
X-Powered-By: ASP.NET
Date: Thu, 19 Nov 2015 20:48:34 GMT
Content-Length: 87

{"Name":"Bu bir isim alanıdır. (tr-TR)","Email":"Bu bir e-posta alanıdır. (tr-TR)"}

Client Accept-Language bilgisinde “tr-TR” yerine “en-US” olarak bir header bilgisi gönderirse eğer, response aşağıdaki gibi oluşacaktır:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?YzpcdXNlcnNcZ8O2a2FscFxkb2N1bWVudHNcdmlzdWFsIHN0dWRpbyAyMDE1XFByb2plY3RzXFdlYkFQSUxvY2FsaXphdGlvblxXZWJBUElMb2NhbGl6YXRpb25cYXBpXGN1c3RvbWVyXA==?=
X-Powered-By: ASP.NET
Date: Thu, 19 Nov 2015 21:05:29 GMT
Content-Length: 81

{"Name":"This is a name field. (en-US)","Email":"This is a email field. (en-US)"}

JSON result’lara baktığımızda ise gelen verinin client’ın istemiş olduğu dil bilgisi doğrultusunda geldiğini görebilmekteyiz. Umarım faydalı bir makale olmuştur. Bu konunun devamına ve daha detaylı incelemesine, çıkacak olan Web API kitabımız üzerinden erişebiliyor olacaksınız. 🙂

Uygulamaya aşağıdan ulaşabilirsiniz.

WebAPILocalization

 

Gökhan Gökalp

View Comments

  • Merhaba Gokhan Bey,

    Daha once yazdiginiz 'token based authentication' yazisindan yararlanarak admin authentication'i gerektiren bir web api projesi uzerinde calisiyorum. Bugun proje' ye buradaki paylasiminizi da okuyarak resource dosyalari ekledim. Resource dosyalarim butun controller class'larim icin sorunsuz calisirken, authentication alirken sadece Turkce mesaj donuyor. Resource dosyalarini defalarca incelememe ve test etmeme ragmen bir sorun goremedim. Acaba authentication class'ina eklemem gereken bir kod parcasi veya benim gozden kacirdigim bir nokta mi var? Gerekli arastirmalari yaptim ancak tatmin edici bir sonuca ulasamadigim icin size yazmaya karar verdim.

    Ilginiz ve paylasimlariniz icin tesekkurler,
    Onur

    • Ayni sekilde WebApiConfig class'ina da bir degisiklige gitmem gerekebilir. Son not olarak projemde Odata' yi kullaniyorum

    • Merhaba, sorununuzu şuan deneyebilecek bir ortamım yok fakat, GrantResourceOwnerCredentials method'unu override ettiğinizde, orada context üzerinde "SetError" method'u kısmında hata mesajını resource'dan aldınız mı ve Header üzerinden Accept-Language bilgisi geliyor mu?

  • merhaba,
    localresources dil dosyaları oluşturdum fakat sayfaların her session için cache den yüklenmesini istiyorum. nasıl yapabilirim?

Recent Posts

Event-Driven Architecture’larda Conditional Claim-Check Pattern’ı ile Event Boyut Sınırlarının Üstesinden Gelmek

{:en}In today’s technological age, we typically build our application solutions on event-driven architecture in order…

2 ay ago

Containerized Uygulamaların Supply Chain’ini Güvence Altına Alarak Güvenlik Risklerini Azaltma (Güvenlik Taraması, SBOM’lar, Artifact’lerin İmzalanması ve Doğrulanması) – Bölüm 1

{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…

9 ay ago

Identity & Access Management İşlemlerini Azure AD B2C ile .NET Ortamında Gerçekleştirmek

{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…

1 yıl ago

Azure Service Bus Kullanarak Microservice’lerde Event’ler Nasıl Sıralanır (FIFO Consumers)

{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…

2 yıl ago

.NET Microservice’lerinde Outbox Pattern’ı ile Eventual Consistency için Atomicity Sağlama

{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…

2 yıl ago