Mert Özen Her Satırda Daha İleri
Backend .NET Core Web API — #13

Global hata yönetimi: Tüm hataları tek merkezde yakalamak

Mert Özen 8 Ağu 2026 12 dk 48 görüntülenme
Global hata yönetimi: Tüm hataları tek merkezde yakalamak

Uygulamanın herhangi bir yerinde oluşan beklenmedik hataları tek bir merkezde yakalamayı işliyoruz: try-catch dağınıklığından kurtulmak, exception middleware kurmak ve kullanıcıya düzgün, güvenli cevap dönmek.

Şimdiye kadar hep işlerin yolunda gittiği senaryolara odaklandık. Ama gerçek dünyada işler yolunda gitmez: veritabanı bağlantısı kopar, beklenmedik bir null ortaya çıkar, bir dönüşüm patlar. Peki bu hatalar oluştuğunda ne oluyor? Bugün, uygulamanın herhangi bir köşesinde patlayan hataları tek bir merkezde yakalamayı ve kullanıcıya düzgün bir cevap dönmeyi konuşuyoruz.

Önce Kötü Yolu Görelim

Hata yönetimini yeni öğrenen birinin ilk refleksi, her action metodunu bir try-catch bloğuyla sarmaktır:

[HttpGet("{id}")]
public async Task GetById(int id)
{
    try
    {
        var user = await _userService.GetByIdAsync(id);
        if (user is null)
        {
            return NotFound();
        }
        return Ok(user);
    }
    catch (Exception ex)
    {
        return StatusCode(500, "Bir hata oluştu.");
    }
}

Bu çalışır ama bir felakete gebe. Düşün: uygulamanda elli tane action metodu var ve her birinin başına aynı try-catch kalıbını kopyalıyorsun. Kod baştan sona tekrar dolu. Bir gün hata cevabının biçimini değiştirmek istediğinde, elli ayrı yeri tek tek düzeltmen gerekecek. Bir tanesini unutursan, o endpoint farklı davranacak. Bu, sürdürülmesi imkânsız bir yol.

Daha İyi Fikir: Tek Bir Merkez

Ya her yere try-catch serpiştirmek yerine, uygulamanın en dışına bir güvenlik ağı gersek? Nereden gelirse gelsin, yakalanmamış her hata bu ağa düşsün ve orada tek bir yerde ele alınsın. İşte global hata yönetiminin fikri budur.

Bunu bir binanın merkezi yangın alarmı gibi düşün. Her odaya ayrı ayrı bekçi koymazsın; bunun yerine binanın tamamını kapsayan tek bir sistem kurarsın. Herhangi bir yerde yangın çıktığında sistem devreye girer, alarmı çalar ve gerekeni yapar. Kod tarafında bu "merkezi sistem", on ikinci yazıda hatırladığımız middleware ile kurulur.

Middleware'i Hatırlayalım

İkinci yazıda middleware'i, gelen her isteğin geçtiği bir dizi kapı olarak tanımlamıştık. Her istek bu kapılardan sırayla geçer. İşte bu boru hattının en başına bir middleware koyarsak, o middleware isteğin geri kalanını sarmalar. Yani sonraki kapılardan herhangi birinde bir hata patlarsa, bizim en baştaki middleware'imiz bunu yakalayabilir. Tıpkı en dıştaki bir try-catch gibi, ama tek seferde tüm uygulama için.

Modern Yol: IExceptionHandler

.NET, bu iş için temiz bir yol sunuyor: IExceptionHandler arayüzü. Bu arayüzü uygulayan bir sınıf yazarsın ve içine, bir hata oluştuğunda ne yapılacağını koyarsın. Şöyle görünür:

public class GlobalExceptionHandler : IExceptionHandler
{
    private readonly ILogger _logger;

    public GlobalExceptionHandler(ILogger logger)
    {
        _logger = logger;
    }

    public async ValueTask TryHandleAsync(
        HttpContext context,
        Exception exception,
        CancellationToken cancellationToken)
    {
        _logger.LogError(exception, "Beklenmeyen bir hata oluştu.");

        context.Response.StatusCode = 500;

        await context.Response.WriteAsJsonAsync(new
        {
            status = 500,
            message = "Sunucuda beklenmeyen bir hata oluştu."
        }, cancellationToken);

        return true;
    }
}

Bu sınıf iki önemli iş yapıyor. Önce hatayı logluyor; bu, sen sorunu sonradan inceleyebilesin diye kritik. Sonra kullanıcıya temiz, JSON formatında bir hata cevabı dönüyor: durum kodu 500 ve anlaşılır bir mesaj. Sondaki return true ise ".NET, bu hatayı ben hallettim, başka bir şey yapmana gerek yok" demek.

Kayıt ve Devreye Alma

Sınıfı yazmak yetmez; onu Program.cs'te tanıtman gerekiyor. İki satırla hallediliyor. Önce servis olarak kaydediyorsun, sonra da boru hattına ekliyorsun:

builder.Services.AddExceptionHandler();
builder.Services.AddProblemDetails();

Bu satırlar builder.Build()'dan önce, servis kayıt aşamasında yer alır. Ardından app tarafında boru hattına ekliyorsun:

app.UseExceptionHandler();

Bu satırı boru hattının başlarına koymak önemli; çünkü ikinci yazıda öğrendiğimiz gibi middleware sırası, isteğin akışını belirler. Hata yakalayıcının, hataların olabileceği diğer middleware'lerden önce gelmesi gerekir ki onları sarmalayabilsin. Artık uygulamanın herhangi bir yerinde yakalanmamış bir hata oluştuğunda, bu merkez devreye girer ve senin action metotların tertemiz kalır.

Neden Ham Hatayı Kullanıcıya Göstermemeliyiz?

Burada kritik bir güvenlik noktası var. Bir hata oluştuğunda .NET'in ürettiği ham hata mesajı, çok fazla iç bilgi içerir: hangi satırda patladığı, hangi sınıfların çağrıldığı, hatta bazen veritabanı yapına dair ipuçları. Bu bilgiyi olduğu gibi dışarı dönmek, kötü niyetli birine uygulamanın iç yapısını hediye etmek demektir.

Bu yüzden merkezi yakalayıcımızda iki şeyi ayırıyoruz: içeride, log'a hatanın tüm detayını yazıyoruz (biz görebilelim diye); dışarıya ise sadece genel, güvenli bir mesaj dönüyoruz. Kullanıcı "sunucuda bir hata oluştu" der, ama hatanın iç detayları asla dışarı sızmaz. Bu ayrım, hem hata ayıklamanı kolaylaştırır hem de uygulamanı güvende tutar.

Küçük Bir Deneme

Yukarıdaki GlobalExceptionHandler'ı projene ekle ve kaydet. Sonra bir action metodunun içine, bilerek hata fırlatan bir satır koy; mesela basitçe throw new Exception("test hatası"); yaz. Bu endpoint'i çağırdığında, artık uygulamanın çökmediğini, bunun yerine senin tanımladığın temiz JSON cevabının döndüğünü göreceksin. Bir de log çıktısına bak: hatanın tüm detayı orada, ama kullanıcıya dönen cevapta yok. Bu deney, merkezi hata yönetiminin hem temizliğini hem de güvenliğini tek seferde gösterir.

Bir sonraki yazıda loglama konusuna geçeceğiz: bu yazıda kullandığımız ILogger'ı derinlemesine ele alacak, Serilog ile yapılandırılmış (structured) loglamanın ne olduğunu ve hataları düzgün kaydetmenin neden bu kadar önemli olduğunu konuşacağız. Bugün logladığımız o hatalar, iyi bir loglama altyapısıyla çok daha değerli hale gelecek.