.NET Middleware ve Global Exception Handling
Giriş
Bu yazı, Global Exception Handling yani program üzerinde çıkabilecek hataları yani istisnaları kontrol etmek ve bu durumu kontrol etmek üzerinedir. Bu yazınında ‘Global’ olmasının sebebi ise her kod satırına Try Catch yazmak yerine bunu tek yerde yönetip loglama gibi durumları orada yapıp programın daha stabil ve kontrol altına almamızı sağlayacaktır. Bunu yapabilmemiz için ilk adım olan Middlewareın ne olduğunu anlamak ile başlayacağız.
Middleware Nedir?
Basit bir şekilde anlatmak gerekirse ASP.NET Core uygulamalarında gelen her requestte bizim belirlediğimiz veya eklediğimiz bileşen araya girer (örneğin, güvenlik kontrolleri, loglama vs.) ve bir sonraki middleware’a iletir. Kısacası web uygulamalarında isteklerin nasıl işlendiğini kontrol etmek için kullanılır. .NET Core ve .NET 5/6/7+ gibi daha modern .NET sürümleri, gelişmiş middleware desteği ve yapılandırma seçenekleri sunar
Senaryo Örneği,
Restoran Örneği: Bir restoranda sipariş veriyorsunuz. Müşterisiniz ve aşçı, yemeği hazırlıyor. Siparişinizi doğrudan aşçıya veremezsiniz. Burada garson, sizin ve aşçı arasında bir köprü görevi görür. Siparişinizi alır, aşçıya iletir, yemeğiniz hazır olduğunda onu size getirir ve ihtiyaç duyduğunuz her şeyde yardımcı olur.
Bu durumda, garson middleware gibi işlev görür. Garson olmadan, sipariş verme ve yemeğinizi alma süreci çok daha karmaşık ve verimsiz olur. Garson, müşterinin isteğini olduğu gibi aşçıya götürmesi, aşçının işini daha çok zorlaştıracaktır. Middleware kavramında tek bir garson olmayabilir. Aynı şekilde, bir web uygulamasında middleware, kullanıcıdan (sizin gibi) gelen istekleri (siparişler) alır, bu istekleri işler (aşçıya ileterek yemeği hazırlatır) ve sonucu kullanıcıya geri döndürür (yemeği size servis eder).
Get Yiğit Özbek’s stories in your inbox
Join Medium for free to get updates from this writer.
Özellikleri:
- Kolaylık ve Düzen
- Güvenlik ve Kontrol
- Esneklik ve Özelleştirme
Kodda nasıl yazıldığını adım adım anlatacağım.
Adım Adım Middleware
Basit bir API projesi üzerinden anlatacağım.
10. satırda olan aşağıdaki kod, Build metodunun çağrılması, tanımlanan tüm middleware bileşenlerinin ve servislerin bir HTTP istek işleme ardışık düzeni oluşturulmasını sağlar. Bu ardışık düzen, gelen isteklerin nasıl işleneceğini belirler. Buradaki eklenecek kodların sırlaması önemlidir.
var app = builder.Build();Örneğin, SecurityMiddleware ve LogMiddleware adında iki tane Middleware oluşturalım.
- Öncelikle classımızı oluşturduktan sonra bu oluşturduğumuz class,
InvokeveyaInvokeAsyncmetoduna sahip olmalıdır.
public class SecurityMiddleware
{
private readonly RequestDelegate _next;
public SecurityMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Middleware'in işlevselliği burada gerçekleştirilir
// Örneğin, gelen istek üzerinde bazı işlemler yapabiliriz
// Sonraki middleware'e geçmeden önce bir işlem yap
await context.Response.WriteAsync("Security Middleware öncesi işlem\n");
// Sonraki middleware bileşenine geç
await _next(context);
// Sonraki middleware'den sonra bir işlem yap
await context.Response.WriteAsync("Security Middleware sonrası işlem\n");
}
}Middleware’imizi oluşturduktan sonra tanılamamız gerekecektir. Aşağıdaki kodu kullanarak ‘Program.cs’e tanımlayarak kullanım sağlayabilirsiniz. Buradaki sırlamanın önemli olduğunu hatırlatmakta fayda var. Atılan HTTP istekte ilk olarak SecurityMiddleware çalışacaktır, tamamlandıtkan sonra LogMiddleware çalışacaktır. Detaylı için YouTube kanalımı ziyaret edebilirsiniz.
app.UseMiddleware<SecurityMiddleware>();Genel Hata Kontrolü
Yukarıdı bölümde nasıl Middleware tanımlayabileceğimizi adım adım açıkladım. Şimdiki bölümde ise aşağıdaki kod satırını açıklayacağım.
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ErrorHandlerMiddleware> _logger;
public ErrorHandlerMiddleware(RequestDelegate next, ILogger<ErrorHandlerMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred: {Message}", ex.Message);
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error. Please try again later."
}.ToString());
}
}Yukarıda gördüğünüz await _next(context);kod satırı Middleware için önemli bir konudur. await _next(context); ifadesi, işlem sırasında mevcut middleware'den sonraki middleware'e geçişi tetikler. Yani Program.cs üzerinde yazılan Middleware’ların sırası çok önemlidir.
await _next(context);Bu yüzden Exception Handling Middleware’i ilk başa yazmanızı öneririm. Önceki Middleware’de Exception almanız durumunda da problem olacaktır. Bundan dolayı ilk tarafa yazmakta fayda var :)
Bonus: Extension
Daha temiz ve okunulabilirlik anlamında bize çok fayda sağlayan extension’u da aşağıdaki şekilde kullanabiliriz. Bu sayede UseMiddleware<ErrorHandlerMiddleware>() yerine UseErrorHandler() yazabiliriz.
using Microsoft.AspNetCore.Builder;
public static class ErrorHandlerMiddlewareExtensions
{
public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ErrorHandlerMiddleware>();
}
}Daha fazlası.
Beni X’den takip edebilirsiniz: https://twitter.com/ygtozbkk





