স্বাগতম! আজকে আমরা আপনার আউটলাইনের #310: Exception Handling Middleware টপিকটি শিখতে যাচ্ছি। এটি অত্যন্ত গুরুত্বপূর্ণ একটি বিষয়।
কখনো কি ভেবে দেখেছেন, Production-এ আপনার অ্যাপ্লিকেশন ক্র্যাশ করলে ইউজার কী দেখে? আজকে আমরা শিখবো কীভাবে পুরো অ্যাপ্লিকেশনের যেকোনো জায়গার Exception-কে একটি নির্দিষ্ট জায়গা থেকে কন্ট্রোল করা যায় এবং ইউজারকে একটি সুন্দর Error Message দেখানো যায়।
📝 Quick Summary (For Future Revision)
- Development Environment: Exception ঘটলে ডিফল্টভাবে Developer Exception Page দেখায় (Stack trace, query string, headers ইত্যাদি সহ)।
- Production Environment: Exception ঘটলে ডিফল্টভাবে খালি HTTP 500 Error দেখায়, কোনো মেসেজ থাকে না।
- Limitation of Exception Filter: Exception Filter শুধুমাত্র Action Method এবং Action Filter-এর Exception ধরতে পারে।
- Global Solution: Exception Handling Middleware ব্যবহার করা হয় পুরো Middleware Pipeline-এর যেকোনো জায়গার Exception ধরার জন্য।
- Core Mechanism: পরবর্তী Middleware-কে কল করার
_nextdelegate-টি একটিtry-catchব্লকের ভেতর রাখা হয়। ফলে পরবর্তী যেকোনো Middleware-এ Exception হলে তা এইcatchব্লকে ধরা পড়ে। - Status Code Update: Exception ক্যাচ করার পর ডিফল্ট Status Code 200 হয়ে যায়, তাই ম্যানুয়ালি
StatusCode = 500সেট করতে হয়।
🧠 Comprehensive Breakdown
১. Default Exception Behavior (Priority: 8/10)
ASP.NET Core-এ যখন কোনো Exception ঘটে, তখন Environment-এর ওপর ভিত্তি করে এটি ভিন্ন আচরণ করে।
- Development Environment:
launchSettings.json-এ environment যদি Development থাকে, তবে ফ্রেমওয়ার্ক আপনাকে Developer Exception Page দেখাবে। এটি ডেভেলপারদের জন্য খুবই দরকারী কারণ এখানে Exception Message, Stack Trace, Routing Information, Request Cookies এবং Headers-এর বিস্তারিত তথ্য থাকে। - Production Environment: Production-এ সিকিউরিটির কারণে এই বিস্তারিত পেজটি দেখানো হয় না। এর বদলে শুধু একটি HTTP 500 Internal Server Error রেসপন্স পাঠানো হয়, যার সাথে কোনো Error Message থাকে না।
২. Why Exception Handling Middleware? (Priority: 10/10)
আপনার মনে হতে পারে, আমরা তো Exception Filter ব্যবহার করেই Error হ্যান্ডেল করতে পারি। তাহলে Middleware কেন?
- The “Why”: Exception Filter-এর স্কোপ খুবই সীমিত। এটি শুধুমাত্র Endpoint Middleware-এর ভেতরে Action Filter বা Action Method এক্সিকিউট হওয়ার সময় ঘটা Exception ধরতে পারে। কিন্তু যদি Routing Middleware বা অন্য কোনো Custom Middleware-এ Exception ঘটে, তবে Exception Filter তা ধরতে পারবে না।
- The Solution: Exception Handling Middleware-কে আমরা Request Pipeline-এর একদম শুরুতে (Top of the pipeline) বসাই। এটি একটি Global Exception Handler হিসেবে কাজ করে।
৩. How Try-Catch Wraps the Pipeline (Priority: 10/10)
এই Middleware-এর মূল ট্রিক হলো try-catch ব্লক। আমরা যখন _next() কল করি, তখন এটি পরবর্তী Middleware-কে কল করে। যদি আমরা এই _next()-কে try-catch-এর ভেতর রাখি, তবে পরবর্তী যতগুলো Middleware কল হবে (Routing, Endpoint, Filters ইত্যাদি), তার সবগুলোর Execution লজিক্যালি এই একটি try ব্লকের আন্ডারে চলে আসবে। ফলে যেকোনো জায়গায় Exception হলে তা আমাদের catch ব্লকে ধরা পড়বে।
৪. Creating Custom Exception Handling Middleware (Video Process) (Priority: 9/10)
ভিডিওতে দেখানো Conventional Middleware তৈরি করার প্রসেসটি নিচে দেওয়া হলো:
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Serilog;
using System;
using System.Threading.Tasks;
namespace CRUDExample.Middleware
{
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
private readonly IDiagnosticContext _diagnosticContext;
public ExceptionHandlingMiddleware(
RequestDelegate next,
ILogger<ExceptionHandlingMiddleware> logger,
IDiagnosticContext diagnosticContext)
{
_next = next;
_logger = logger;
_diagnosticContext = diagnosticContext;
}
public async Task Invoke(HttpContext httpContext)
{
try
{
// Calling the subsequent middleware
await _next(httpContext);
}
catch (Exception ex)
{
// 1. Log the Exception
if (ex.InnerException != null)
{
_logger.LogError("{ExceptionType} {ExceptionMessage}",
ex.InnerException.GetType().ToString(),
ex.InnerException.Message);
}
else
{
_logger.LogError("{ExceptionType} {ExceptionMessage}",
ex.GetType().ToString(),
ex.Message);
}
// 2. Set Status Code to 500
httpContext.Response.StatusCode = 500;
// 3. Write Custom Response
await httpContext.Response.WriteAsync("Error occurred. Please try again.");
}
}
}
}
Why reset the Status Code?
Exception ক্যাচ করার পর ASP.NET Core মনে করে Error-টি সফলভাবে হ্যান্ডেল করা হয়েছে, তাই সে ডিফল্টভাবে Status Code 200 OK সেট করে দেয়। ব্রাউজার বা ক্লায়েন্টকে সঠিক মেসেজ দেওয়ার জন্য আমাদের ম্যানুয়ালি httpContext.Response.StatusCode = 500 সেট করতে হয়।
৫. Registering the Middleware (Priority: 10/10)
Program.cs ফাইলে এটিকে Pipeline-এর শুরুর দিকে অ্যাড করতে হবে।
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// Adding our custom middleware for Production/Staging
app.UseMiddleware<ExceptionHandlingMiddleware>();
}
৬. The Modern .NET 8 / .NET 10 Approach (Smarter & Updated) (Priority: 10/10)
ভিডিওতে দেখানো পদ্ধতিটি ক্লাসিক এবং চমৎকার। তবে আধুনিক .NET 8 এবং পরবর্তী ভার্সনগুলোতে (.NET 10 সহ) Global Exception Handling-এর জন্য একটি বিল্ট-ইন ইন্টারফেস নিয়ে আসা হয়েছে, যার নাম IExceptionHandler। এটি আরও ক্লিন এবং মডার্ন অ্যাপ্রোচ।
Updated Implementation:
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
public class GlobalExceptionHandler : IExceptionHandler
{
private readonly ILogger<GlobalExceptionHandler> _logger;
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
{
_logger = logger;
}
public async ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
_logger.LogError(exception, "An unexpected error occurred: {Message}", exception.Message);
httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
await httpContext.Response.WriteAsync("Error occurred. Please try again.", cancellationToken);
// Return true to indicate the exception has been handled
return true;
}
}
Program.cs -এ রেজিস্ট্রেশন:
// Register the service
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
var app = builder.Build();
// Add middleware to the pipeline
app.UseExceptionHandler(opt => { });
⌨️ Shortcuts Mentioned
- Browser Developer Tools / Network Tab: ব্রাউজারে Network Request এবং Status Code (যেমন 500 Error) চেক করার জন্য
Ctrl + Shift + Iচাপুন। - Bonus (Visual Studio / VS Code): নতুন ফাইল বা ক্লাস তৈরি করার জন্য Visual Studio-তে
Ctrl + Shift + Aএবং VS Code-এCtrl + Nব্যবহার করতে পারেন।
🏆 Best Practices (Exception Handling)
- Never Expose Stack Traces in Production: ক্লায়েন্টকে কখনোই প্রকৃত Error Message বা Stack Trace দেখাবেন না। এটি হ্যাকারদের কাছে সিস্টেমের দুর্বলতা প্রকাশ করে দেয়। সবসময় “Something went wrong” জাতীয় জেনেরিক মেসেজ দিন।
- Log Everything: Exception হ্যান্ডেল করার পাশাপাশি অবশ্যই তা Log করবেন (যেমন Serilog ব্যবহার করে)। Exception-এর ধরণ (
GetType()) এবং মেসেজ (Message) ডাটাবেস বা ফাইলে সেভ করে রাখুন। - Use ProblemDetails for Web API: আপনি যদি Web API নিয়ে কাজ করেন, তবে প্লেন টেক্সট রিটার্ন করার বদলে
ProblemDetailsস্ট্যান্ডার্ড (RFC 7807) ফলো করে JSON ফরম্যাটে Error Response পাঠানো বেস্ট প্র্যাকটিস।
// Example for Web API
await httpContext.Response.WriteAsJsonAsync(new ProblemDetails
{
Status = 500,
Title = "Server Error",
Detail = "An unexpected error occurred."
});