ASP.NET Core, ortam değişkenini kullanarak çalışma anında uygulamanın davranışlarını yapılandırır.
Projemizin bulunduğu ortama dayalı olarak davranışlarını kontrol etmek, kabaca ortamına göre hareket etmek isteriz. Buradan yola çıkarak uygulamalarımızın son kullanıcıya ulaşmadan önce test edildiği, görüntülendiği ya da incelendiği birden çok evre olacaktır. Eğer birden çok ortamın yoksa acilinden 🚨 ortamlarını oluşturmalısın. Konuyu dağıtmadan bu ortamlar, net core uygulamalarında varsayılan (default) olarak gelen “Development”, “Staging (Pre-production)” , “Production” ve kendimizin ekleyebileceği/özelleştirebileceği ortamlardır.
İçeriğimizin başrolü olan ve kendisinden fazla söz edeceğimiz “ASPNETCORE_ENVIRONMENT” isimlendirmesine sahip değişkenini kenara altı çizili şekilde yazmak istiyorum. Bu değişken ile projemiz çalışma anında (runtime) hangi ortam koşullarına göre davranacağını belirliyoruz. Bir üst paragrafta bahsettiğim varsayılan ortamlar haricinde kendimizin ekleyebileceği/özelleştirebileceği, örneğin “Alfa”, “Beta”, “Test”, “Oldu mu Acaba?”, “Son Test” gibi isimlendirmelerle ekstra ortamlar da oluşturabiliyoruz.
Varsayılan olarak, projemizi localhost’umuzda çalıştırdığımızda “Development”, publish ettiğimizde ise “Production” ortamda çalışmaktadır. Bu işin doğasında var!
Ortam Değişkenlerine Neden/Nasıl İhtiyaç Duyarım?
Development ortamında hata sayfalarına ihtiyaç duyarız çünkü hata aldığımızda hata detayını görüntülemek bizi çözüme hızlı 🚀şekilde götürür. Staging (Pre-production)/Production ortamda ise kullanıcı hata aldığında hatayla ilgili ayrıntıyı göstermek yerine öncesinde hazırlamış olduğumuz hata sayfasına yönlendiririz. Basite indirgemem gerekirse development ortamında test veri tabanı bağlantı dizisini kullanırken, staging ve production ortamda canlı veri tabanı bağlantı dizisini kullanmak isteriz.
Aşağıda varsayılan (default) olarak gelen “/Properties/launchSettings.json” dosya içeriğini görüyoruz. JSON dosyasını incelediğimizde “ASPNETCORE_ENVIRONMENT” genel değişkeninin (global variable) “Development” olduğunu yani Kestrel/IIS üzerinde çalışacak projenin Development ortamda ayağa kalkacağını bildiriyoruz.
Projeyi Farklı Ortamlarda Nasıl Çalıştırabilirim?
Net Core projeleri cross platform (çapraz platform yazılımı) olduğundan dolayı ister IIS Express istersek de Kestrel sunucu üzerinde çalıştırabiliriz. Kestrel, herhangi bir platformda ASP.NET uygulamalarını barındırmak için kullanılan açık kaynaklı kodlu, olaya dayalı ve eşzamansız, I/O tabanlı bir sunucudur.
Her profilin içerisinde bulunan commandName anahtarı 🔑 ile projeyi IISExpress veya Kestrel web sunucusu üzerinde çalıştıracağımızı, ASPNETCORE_ENVIRONMENT değişkeniyle de profilin hangi ortamda olacağını belirliyoruz.
Windows ve macOS üzerinde ortam değişkenlerinin büyük/küçük harf duyarlılığı (case sensitive) yoktur fakat Linux üzerinde büyük/küçük harf duyarlılığı vardır.
Projeyi hangi ortamda başlatmak istiyorsak run kısmından ilgili seçeneği seçiyoruz fakat burada dikkat etmemiz gereken önemli bir husus var. 📌Her ortamın kendine ait ortam değişkeni (environment variables) bulunduğundan dolayı farklı ortamlarda farklı ortam değişkenleri kullanıyorsanız bunları mutlaka her ortam için ayrı ayrı tanımlamalısınız.
Yaşam Döngüsü (lifecycle) 🌏
Proje çalışma anında “ASPNETCORE_ENVIRONMENT” değerini okur ve IHostEnvironment.EnvironmentName string değişkenine aktarır. Peki bunun yanı sıra “SonTest” diye bir ortam oluşturmuş olsaydım bunu nasıl yönetebilirdim? Diyorsanız bir örnek vermek isterim.
- ASPNETCORE_ENVIRONMENT genel değişkeninin değeri “Development” ise UseDeveloperExceptionPage() ara katmanı (middleware) istek akışında ilk sırada devreye girecek ve son olarak da bu ara katmandan çıkış yapacaktır.
- Eğer “SonTest” ise UseStatusCodePages() ara katmanı devreye girecektir.
- Eğer “Development” ya da “SonTest” değilse UseExceptionHandler() ara katmanı devreye girerek belirlemiş olduğumuz dosya yolu(/Home/Error) hata anında görüntülenecektir.
Veya bir farklı örnek daha vermem gerekirse .cshtml tarafında bu environment değerlerine göre koşul koşabileceğimiz yardımcı tag helper’larımız bulunmaktadır.
Örneğin bundle edilmiş JS/CSS dosyalarınını “Staging, Production” ortamında yayınlayabilir, bundle edilmemiş dosyalarınızı ise Development ortamında yayınlayabilirsiniz.
IWebHostEnvironment
Uygulamanın herhangi bir noktasında ortam değişkenine ulaşmak istiyorsak IWebHostEnvironment ara yüzü (interface) ile çalışma anında (runtime) EnvironmentName’e ulaşabiliriz. IWebHostEnvironment interface net core çekirdeğinde dahili gelen arayüzdür ve IOC mekanizmasında direkt inject 💉 edilip kullanılabilir durumdadır.
Uygulamanın environmentlarını launchSettings.json dosyasından yönetebildiğimiz gibi Debug penceresinden de yönetebiliriz. Burada tanımladığımız her değer launchSettings.json dosyasına yansımaktadır. Tabii yönetim esnasında Profile kısmından hangi profil için işlem yapmak istediğimizi de seçmeyi unutmamalı, launch kısmından ise uygulamanın nereden ayağa kalkacağını (IIS&Kestrel) belirlemeliyiz.
Environment Ne Zaman Devreye Giriyor?
Genelde proje içerisinde ki sabit yani değişmeyecek değerleri birer key-value şeklinde best pratices açısından appsettings. json içerisinde tanımlar (Örn: Veri tabanı yolu, API Kullanıcı adı veya şifresi, veya herhangi bir sabit değer.) IConfiguration interface ile okuruz. Dikkat edilmesi gereken en önemli husus environment değişkenlerin secret.json ve appSettings.{environmentName}. json dosyalarını ezmesidir.💡 Yani bir değişken hem appsettings. json hem secret. json hem de environment variables olarak tanımlandıysa buradaki yaşam döngüsü şu şekilde ilerler: Önce ilgili isimlendirmeye ait environment değişken var mı? Ona bakar. Eğer yoksa secret. json içerisine bakar burada da bulamazsa appsettings. json üzerinde arar. Anlaşılacağı üzere öncelik sırası vardır ve öncelik her zaman environment variables’dadır.
Yaşam Döngüsü: Environment variables > secret.json > appsettings.json
Development ortamda geliştirme yaparken genelde local veri tabanı kullanır, ConnectionStrings değerine de localde çalıştığımız veri tabanı yolunu veririz.
Oluşturacağım ortama göre appsettings.json dosyası oluşturabiliriz.
- appsettings.Development.json
- appsettings.Production.json
Uygulamamızı production ortama aktardığımızda appsettings.Production.json içerisine canlı veri tabanı yolunu yazar, belirlediğimiz ortama göre de değerleri alırız.
Üzgünüm!!! Güvenliği Atladık!
Production ortamda veri tabanı yolu gibi kritik bilgileri appsettings.json dosyasında tutmak güvenlik zafiyeti 👮 oluşturacaktır. Kritik veriler her daim environment’da tutulmalıdır. Çünkü environmentlar çıktı olarak production ortama gönderilmez yani publish aldığın zaman çıktı dosyalarında bulunmaz fakat JSON dosyaları bulunur.
Aslında uygulamayı hangi ortamda publish ediyorsanız edin, appsettings.{environmentName}.json dosya/dosyalarında güvenlik zafiyeti oluşturabilecek key-value değerleri bulunmamalıdır. Çünkü File Transfer Protocol (FTP)’yi 📂 yani uygulamanın dosya dizinini ele geçiren/geçirebilecek kişi appsettings dosyamızdaki bilgileri ele geçirebilir. (Örneğin: veri tabanı bilgilerini)
Konuyu daha da basitleştirecek olursak, github🐒 üzerinde proje geliştiriyorsanız appsettings.json içerisinde güvelik zafiyeti oluşturacak bilgiler tutmamalıyız! Bunun yerine projenin Manage User Secrets özelliğinden faydalanmalıyız. Uzak sunucuda ise Environment değişkenler kullanarak hosting’de ya da cloud’da ☁️ bu değerleri tanımlamalıyız.
Manage User Secrets hakkında bilgiyi daha önceki yazım olan The Protector: NetCore #S1B1 edinebilirsiniz.
Şöyle toparlayacak olursak, .net core uygulaması ayağa kalktığında yani çalışır vaziyette iken IConfiguration üzerinden appsettings içerisindeki herhangi değere ulaşmaya çalıştığımda öncelikle ulaşmaya çalıştığım KEY ile ilgili environment değişken var mı? Buna bakar. Eğer varsa environment’dan değeri okumaya çalışır, yoksa secret.json’a bakar daha sonra appsettings içerisinde eşleşen key varsa value okumaya çalışır. Anlaşılan üzere enviroment değerleri appsettings içerisindeki değerleri ezer.
Windows’ta Global Olarak Environment Değişken Nasıl Tanımlarım?
İlginizi Çekebilecek Diğer Kaynaklar
- Safe storage of app secrets in development in ASP.NET Core
- Use multiple environments in ASP.NET Core
- Environment Tag Helper in ASP.NET Core
Ci vediamo 😎