এখানে ASP.NET Core এর অত্যন্ত গুরুত্বপূর্ণ একটা টপিক—Middleware নিয়ে কথা বলা হয়েছে। একজন .NET ডেভেলপার হিসেবে মিডলওয়্যার কীভাবে কাজ করে, সেটা একদম ক্লিয়ার থাকা বাধ্যতামূলক। চলো ট্রান্সক্রিপ্টের প্রতিটি কথা ভেঙে ভেঙে প্র্যাকটিক্যাল কোডসহ আলোচনা করি।

Topic Overview
এই লেকচারে মূলত Middleware কী, ASP.NET Core এর Request Pipeline কীভাবে কাজ করে, Single Responsibility Principle (SRP) কীভাবে মিডলওয়্যারে অ্যাপ্লাই করা হয় এবং কীভাবে কাস্টম মিডলওয়্যার তৈরি করতে হয়—তা নিয়ে আলোচনা করা হয়েছে। এছাড়া Terminal বা Short-circuiting মিডলওয়্যারের কনসেপ্টও কভার করা হয়েছে।

Detailed Breakdown
ট্রান্সক্রিপ্টে বলা প্রতিটি পয়েন্টের বিস্তারিত ব্যাখ্যা এবং কোড ইমপ্লিমেন্টেশন নিচে দেওয়া হলো:
১. Middleware কী এবং Pipeline কীভাবে কাজ করে?
ট্রান্সক্রিপ্টের পয়েন্ট: মিডলওয়্যার হলো এমন কিছু কম্পোনেন্ট, যা অ্যাপ্লিকেশন পাইপলাইনে যুক্ত করা হয় রিকোয়েস্ট (Request) এবং রেসপন্স (Response) হ্যান্ডেল করার জন্য। এগুলো মূলত পরপর সাজানো কিছু মেথড।
ব্যাখ্যা: যখন ব্রাউজার থেকে কোনো রিকোয়েস্ট তোমার সার্ভারে আসে, সেটা সরাসরি তোমার কন্ট্রোলারে বা এপিআই-তে হিট করে না। রিকোয়েস্টটা একটা পাইপের ভেতর দিয়ে যায়। এই পাইপের ভেতর বিভিন্ন ধাপে চেকিং বা মডিফিকেশন হয়। এই একেকটা ধাপই হলো এক একটা মিডলওয়্যার। সব মিডলওয়্যারের কাজ শেষ হলে রেসপন্স আবার একই পথ দিয়ে ব্রাউজারে ফিরে যায়।
২. Sequence বা ক্রমানুসারে এক্সিকিউশন
ট্রান্সক্রিপ্টের পয়েন্ট: পাইপলাইন শুরুতে ফাঁকা থাকে। এরপর একে একে মিডলওয়্যার অ্যাড করা হয়। যে অর্ডারে বা ক্রমানুসারে তুমি কোডে মিডলওয়্যারগুলো অ্যাড করবে, ঠিক সেই সিকোয়েন্সেই তারা এক্সিকিউট হবে।
Priority: High Priority! মিডলওয়্যারের সিকোয়েন্স ঠিক রাখাটা .NET-এ সবচেয়ে বেশি গুরুত্বপূর্ণ। অর্ডারিং ভুল হলে পুরো অ্যাপ্লিকেশন ক্র্যাশ করতে পারে বা সিকিউরিটি রিস্ক তৈরি হতে পারে।
৩. Single Responsibility Principle (SRP)
ট্রান্সক্রিপ্টের পয়েন্ট: প্রতিটি মিডলওয়্যার শুধু একটা নির্দিষ্ট কাজ করে। যেমন: প্রথমটি HTTPS রিডিরেকশন, দ্বিতীয়টি Static Files, তৃতীয়টি Authentication এবং চতুর্থটি Authorization। এর সুবিধা হলো, তুমি চাইলে যেকোনো একটা মিডলওয়্যার বাদ দিয়ে দিতে পারো, এতে অন্যগুলোর কোনো সমস্যা হবে না।
C# Code Implementation (SRP in built-in middlewares):
নিচের কোডটি দেখো, আধুনিক .NET (6+) এ Program.cs ফাইলে ঠিক এভাবেই পরপর মিডলওয়্যারগুলো সাজানো থাকে:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// 1. HTTPS Redirection Middleware (শুধু এই কাজটাই করবে)
app.UseHttpsRedirection();
// 2. Static Files Middleware (HTML, CSS, JS ফাইল সার্ভ করবে)
app.UseStaticFiles();
// 3. Authentication Middleware (ইউজার কে, সেটা আইডেন্টিফাই করবে)
app.UseAuthentication();
// 4. Authorization Middleware (ইউজারের এই কাজটা করার পারমিশন আছে কিনা চেক করবে)
app.UseAuthorization();
// সবার শেষে এন্ডপয়েন্ট (যেখানে আসল লজিক থাকে)
app.MapGet("/", () => "Hello Hasib! Pipeline completed.");
app.Run();
৪. মিডলওয়্যার তৈরির দুটি উপায়
ট্রান্সক্রিপ্টের পয়েন্ট: মিডলওয়্যার দুইভাবে বানানো যায়: ১. Request Delegate (Anonymous মেথড বা Lambda Expression) দিয়ে (ছোট কাজের জন্য) এবং ২. আলাদা ক্লাস তৈরি করে (বড় লজিকের জন্য)।
C# Code Implementation (Way 1: Lambda Expression / Inline):
ছোটখাটো লজিক বা টেস্টিংয়ের জন্য app.Use ব্যবহার করে ইনলাইন মিডলওয়্যার বানানো যায়।
app.Use(async (context, next) =>
{
// Request আসার সময় এই অংশটুকু কাজ করবে
Console.WriteLine("1. Request is entering my inline middleware.");
// next() কল করার মানে হলো রিকোয়েস্টকে পরবর্তী মিডলওয়্যারের দিকে ঠেলে দেওয়া
await next.Invoke();
// Response ফিরে যাওয়ার সময় এই অংশটুকু কাজ করবে (Russian Doll effect)
Console.WriteLine("2. Response is going back through my inline middleware.");
});
C# Code Implementation (Way 2: Separate Class): বড় কাজের জন্য ক্লাস ব্যবহার করাটা প্রফেশনাল।
// CustomMiddleware.cs
public class CustomLoggerMiddleware
{
private readonly RequestDelegate _next;
// কনস্ট্রাক্টরের মাধ্যমে পরবর্তী মিডলওয়্যারকে রিসিভ করা হয়
public CustomLoggerMiddleware(RequestDelegate next)
{
_next = next;
}
// InvokeAsync মেথডেই আসল কাজটা হয়
public async Task InvokeAsync(HttpContext context)
{
// 1. Request Pipeline logic
Console.WriteLine($"Request received for: {context.Request.Path}");
// 2. Call the next middleware in the pipeline
await _next(context);
// 3. Response Pipeline logic
Console.WriteLine($"Response sent with status code: {context.Response.StatusCode}");
}
}
// Program.cs এ এটাকে অ্যাড করার নিয়ম:
// app.UseMiddleware<CustomLoggerMiddleware>();
৫. Terminal বা Short-circuiting Middleware
ট্রান্সক্রিপ্টের পয়েন্ট: এমন কোনো নিয়ম নেই যে একটা মিডলওয়্যারকে পরবর্তী মিডলওয়্যারের কাছে রিকোয়েস্ট পাঠাতেই হবে। যদি কোনো মিডলওয়্যার পরবর্তী কাউকে রিকোয়েস্ট না পাঠায়, তবে তাকে Terminal বা Short-circuiting মিডলওয়্যার বলে।
Priority: Medium Priority. মাঝে মাঝে কন্ডিশনাল চেকিংয়ের জন্য এটা কাজে লাগে।
C# Code Implementation (Terminal Middleware):
app.Run ব্যবহার করলে বা next() কল না করলে পাইপলাইন ওখানেই শেষ হয়ে যায়।
app.Use(async (context, next) =>
{
if (context.Request.Path == "/block-me")
{
// Short-circuiting: আমরা next() কল করছি না।
// তাই রিকোয়েস্ট আর সামনের দিকে যাবে না, এখান থেকেই রেসপন্স ব্যাক করবে।
context.Response.StatusCode = 403; // Forbidden
await context.Response.WriteAsync("You are blocked by Terminal Middleware!");
return;
}
// কন্ডিশন না মিললে নরমালি সামনের দিকে যাবে
await next();
});
// app.Run() সবসময় Terminal হিসেবে কাজ করে। এর পরে আর কোনো মিডলওয়্যার এক্সিকিউট হয় না।
app.Run(async context =>
{
await context.Response.WriteAsync("This is the end of the pipeline.");
});
Added Context (Gaps Filled)
ট্রান্সক্রিপ্টের বাইরেও কিছু জিনিস তোমার জানা দরকার, যাতে কনসেপ্টটা একদম সলিড হয়:
- ইন্টারনাল মেকানিজম (The Russian Doll Model): মিডলওয়্যারগুলো অনেকটা রাশিয়ান পুতুলের মতো কাজ করে। রিকোয়েস্ট যখন আসে, তখন প্রথম মিডলওয়্যার থেকে শুরু হয়ে ভেতরের দিকে (পরবর্তী মিডলওয়্যারে) যায়। আর রেসপন্স যখন ফেরে, তখন উল্টো দিক থেকে (শেষ মিডলওয়্যার থেকে প্রথমটির দিকে) ফিরে আসে।
- Trade-offs (সুবিধা-অসুবিধা): বেশি মিডলওয়্যার মানে রিকোয়েস্ট প্রসেস হতে বেশি সময় লাগবে। তাই অপ্রয়োজনীয় মিডলওয়্যার পাইপলাইনে রাখা উচিত নয়।
- কেন
app.Useএবংapp.Runআলাদা?app.Useপরবর্তী মিডলওয়্যারকে কল করার সুযোগ দেয় (next()এর মাধ্যমে)। কিন্তুapp.Runহলো ডেড-এন্ড। এর কাজই হলো পাইপলাইন শেষ করে দেওয়া।
C# Best Practices
একজন প্রফেশনাল .NET ডেভেলপার হিসেবে এই প্র্যাকটিসগুলো সবসময় মেনে চলবে:
১. Use Extension Methods for Registration:
ক্লাস-বেসড মিডলওয়্যার সরাসরি app.UseMiddleware<CustomLoggerMiddleware>() দিয়ে কল না করে, একটা Extension মেথড বানিয়ে নেওয়া স্ট্যান্ডার্ড প্র্যাকটিস।
// Extension Method
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseCustomLogger(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomLoggerMiddleware>();
}
}
// Program.cs এ ব্যবহার
// app.UseCustomLogger();
২. Strict Ordering (অর্ডারিং রুলস): Microsoft-এর রেকমেন্ডেশন অনুযায়ী মিডলওয়্যার অ্যাড করার একটা নির্দিষ্ট সিকোয়েন্স আছে। সবসময় এটা ফলো করবে:
UseExceptionHandler(সবার আগে, যাতে ভেতরের সব এরর ধরতে পারে)UseHstsUseHttpsRedirectionUseStaticFilesUseRoutingUseCorsUseAuthenticationUseAuthorization(অবশ্যই Auth এর পরে বসবে)MapControllers/ Endpoints
৩. Keep it Lightweight: মিডলওয়্যারের ভেতরে ডাটাবেস কোয়েরি বা ভারী প্রসেসিং যতটা সম্ভব এড়িয়ে চলবে, কারণ প্রতিটি রিকোয়েস্ট এই মিডলওয়্যারগুলোর ভেতর দিয়ে যায়। এখানে ভারী কাজ করলে পুরো অ্যাপ্লিকেশনের পারফরম্যান্স ড্রপ করবে।
হাসিব, কনসেপ্টটা ক্লিয়ার হলো? এই টপিকটার ওপর কি কোনো স্পেসিফিক রিয়েল-লাইফ সিনারিও দিয়ে আরেকটা কোড এক্সাম্পল চাও?