ASP.NET Core 6/İstisna ve Hata Yönetimi
Bu bölümde uygulamamızın çalışması esnasında meydana gelen çalışma zamanı hatalarında uygulamamızın ne yapacağını belirleyeceğiz. Şimdi yeni bir proje oluşturun ve projenin Program.cs dosyası şöyle olsun:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(context => {
throw new Exception("Something has gone wrong");
});
app.Run();
Bu program bilinçli bir şekilde hata oluşturmaktadır. Programımızı çalıştırdığımızda ASP.NET Core'un oluşan hata hakkında detaylı bilgi verdiğini görürüz. Bu bilgiler arasında hatanın hangi satırda meydana geldiği, fırlatılan hatanın tipi ve metni, hatayı oluşturan request'in query string'i, cookie'leri, rotası ve hata oluştuğu esnada yürütülmekte olan endpoint bulunmaktadır. Kuşkusuz bu bilgiler geliştirici açısından çok değerlidir.
Ancak uygulamanın geliştirme süreci launchSettings.json dosyası üzerinden Development'tan Production'a çekilirse aynı hatada sunucunun sadece HTTP 500 hata kodu döndürdüğünü görürüz. Bu mantıklıdır, çünkü eğer uygulama canlıya geçtiyse kullanıcıların oluşan hata hakkında detaylı bilgi sahibi olmasına gerek yoktur, dahası güvenlik riski oluşturabilir.
Kendi hata sayfamızı tanımlama
değiştirProduction aşamasında uygulamamızda bir çalışma zamanı hatası olduğunda ASP.NET Core sadece HTTP 500 kodunu döndürür. Uygulamamızda bir çalışma zamanı hatası olduğunda sadece durum kodu döndürmek yerine kendi yazdığımız bir hata sayfasının gösterilmesini sağlayabiliriz. Örnek (Program.cs dosyası):
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/error.html");
app.UseStaticFiles();
}
app.Run(context => {
throw new Exception("Something has gone wrong");
});
app.Run();
Bu kod eğer aktif geliştirme süreci Development değilse belirli bir html dosyasının hata durumunda gösterilmesini sağlamaktadır. Programımız Development aşamasındaysa geliştirici dostu hata sayfası kullanılacaktır. Hata sayfasını wwwroot klasöründe ve error.html isminde oluşturun, içeriği şöyle olsun:
<!DOCTYPE html>
<html>
<head>
<title>Hata</title>
</head>
<body>
<h1>Bir Hata Oluştu!</h1>
<p>Uygulamada bizim test etmeyi unuttuğumuz bir hata oluştu. <a href="/">Ana sayfaya</a> dönmeyi deneyebilirsiniz.</p>
</body>
</html>
Bu, olabilecek en basit HTML sayfalarından biridir. Hata sayfasında bootstrap stilleri ve resimler olabilir. Ancak daha karmaşık ASP.NET Core özellikleri kullanılmamalıdır. Hataya neden olan şey hata sayfasının gösterilmesine engel olmamalıdır. Ayrıca uygulamanın tamamında oluşan hataları yakalayabilmesi için UseExceptionHandler() middleware'inin request pipeline'ın en başında tanımlanan middleware olması gerekmektedir.
Durum kodu cevaplarını zenginleştirme
değiştirUygulamamızın hata döndürdüğü durumlar yalnızca uygulamamızda meydana gelen çalışma zamanı hataları olmak zorunda değildir. Bazen çalışma zamanı hatası oluşmamasına rağmen geriye bir hata döndürmek isteyebiliriz. Örneğin kullanıcının aradığı bir kaynak bulunamadığı zaman veya ilgili kaynağa erişim yetkisinin olmadığı zaman da bir hata döndürebiliriz. Ayrıca UseExceptionHandler() middleware'i bir çalışma zamanı hatası oluştuğunda ilgili html dosyasına bir yönlendirme cevabıyla geri döner. Kullanıcı tekrar ilgili html sayfasına talepte bulunur ve hata oluştuğunu söyleyen HTML sayfası ekranda belirir. Bu kullanıcı açısından mantıklıdır, nihayetinde hata oluştuğunu söyleyen sayfayı ekranda görmektedir.
Ancak web uygulamamız web servis mantığında kullanılıyorsa yukarıdaki hata belirtme yaklaşımı yanlıştır. Servisi tüketen program ilk önce yönlendirme cevabı (HTTP 307), ardından başarılı (HTTP 200) cevabı alacaktır. Bu da programın servisten yapmasını istediği işlemin yapıldığı kanısına ulaşmasına yol açabilir. Yapmamız gereken şey çalışma zamanı hatası olmayan bir hata oluştuğunda yönlendirme yapmadan istediğimiz hata kodunu döndürebilmek ve sayfanın içeriğini de istediğimiz gibi ayarlayabilmektedir. Bu sayede hem servis tüketicilerine hem de gerçek insanlara doğru mesaj vereceğiz. Şimdi projemize ResponseString.cs isminde ve içeriği aşağıdaki gibi olan bir sınıf ekleyelim:
public static class Responses
{
public static string DefaultResponse = @"
<!DOCTYPE html>
<html>
<head>
<title>Hata</title>
</head>
<body>
<h1>{0} Hatası Oluştu!</h3>
<p>
Bir hata oluştu. <a href=""/"">Ana sayfaya</a> dönmeyi deneyebilirsiniz.
</p>
</body>
</html>";
}
Bu sınıf DefaultResponse alanı üzerinden erişilen bir HTML şablon string'i içermektedir. HTML kodu kullanıcıyı bir hata oluştuğuna dair bilgilendirmektedir. Hata kodu, {0} yazan yere dışarıdan enjekte edilecektir. Şimdi Program.cs dosyasını şöyle değiştirelim:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/error.html");
app.UseStaticFiles();
}
app.UseStatusCodePages("text/html", Responses.DefaultResponse);
app.Use(async (context, next) => {
if (context.Request.Path == "/error")
{
context.Response.StatusCode = StatusCodes.Status404NotFound;
await Task.CompletedTask;
}
else
{
await next();
}
});
app.Run();
Bu programda /error path'ına bir talep gelmesi durumunda ilgili middleware geriye HTTP 404 kodunu göndermekte ve request pipeline'a kısa devre yaptırmaktadır. Gönderilen 404 kodu dönüş yolunda UseStatusCodePages() middleware'i tarafından tespit edilir. Bu middleware döndürülen durum kodunu değiştirmeden ilgili değişkenle belirtilen HTML kodlarını response'a yazar, ilgili string değişkende {0} ile belirtilen yere durum kodunu yazar, response'un ContentType özelliğini "text/html" olarak ayarlar. Elde ettiğimiz şey servis tüketicileri ve gerçek insanlar tarafından anlaşılan hata mesajlarıdır. Bir endpoint veya middleware'in bir hata belirtmesi için tek yapması gereken response'un durum kodunu ayarlamaktır. Gerisi UseStatusCodePages() middleware'i tarafından halledilmektedir.
UseStatusCodePages() middleware'i;
- yalnızca durum kodu 400-600 arasında olan response'lara müdahale etmektedir.
- Durum kodu 400-600 arasında olsa bile zaten response'a yazma yapılmışsa response'a dokunmamaktadır.
- Çalışma zamanı hatası sonucu oluşan durum kodlarıyla ilgilenmemektedir. Bu amaç için UseExceptionHandler() middleware'i kullanılmalıdır.
- Yalnızca bizim tarafımızdan gönderilen hata kodlarında değil, sistem tarafından gönderilen hata kodlarında da devreye girmektedir. Örneğin hiçbir endpoint veya middleware tarafından response'a yazma yapılmamışsa ASP.NET Core otomatik olarak geriye HTTP 404 hata kodunu gönderir. Böyle bir durumda da UseStatusCodePages() middleware'i devreye girmekte ve daha şık bir hata sayfasının gönderilmesini sağlamaktadır.