স্বাগতম! আমরা এখন আপনার কোর্সের Section 20 (Filters)-এর “Short Circuiting Action Filter” (লেকচার ২৯৩) টপিকে আছি।
আগের লেকচারগুলোতে আমরা দেখেছি ফিল্টার কীভাবে কাজ করে এবং কীভাবে ফিল্টার থেকে অ্যাকশন মেথডে কন্ট্রোল যায়। কিন্তু যদি এমন হয়, ইউজারের দেওয়া ডেটাই ভুল, তাহলে কি রিকোয়েস্টটাকে অ্যাকশন মেথড পর্যন্ত যেতে দেওয়া উচিত? মোটেও না! আজ আমরা শিখব কীভাবে ফিল্টারের ভেতর থেকেই রিকোয়েস্ট বাতিল করে ইউজারকে রেসপন্স পাঠিয়ে দেওয়া যায়, যাকে বলা হয় Short-Circuiting।
📝 Quick Revision Summary
ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য আজকের লেকচারের মূল পয়েন্টগুলো নিচে দেওয়া হলো:
- Short-circuiting কী: Action Method এবং পরবর্তী Filter-গুলোকে এক্সিকিউট হতে না দিয়ে সরাসরি Filter থেকেই রেসপন্স (Result) ব্রাউজারে পাঠিয়ে দেওয়া।
- কেন করব: মূলত Validation Error বা ইনভ্যালিড ইনপুটের ক্ষেত্রে Action Method-কে অপ্রয়োজনীয় এক্সিকিউশনের হাত থেকে বাঁচাতে।
- Code Reusability: Controller-এর
CreateএবংEditমেথড থেকে বারবারModelState.IsValidচেক করার কোডটি মুছে ফেলে, সেটি একটি কমন ফিল্টারে নিয়ে যাওয়া। - কীভাবে শর্ট-সার্কিট করতে হয়:
context.Result-এ কোনো নন-নাল (non-null) ভ্যালু (যেমন:ViewResult,BadRequestResult) অ্যাসাইন করতে হয়।await next()মেথডটি কল করা থেকে বিরত থাকতে হয়।
- Result Filter-এর উপর প্রভাব: Action Filter শর্ট-সার্কিট করলে Action Method কল হয় না ঠিকই, তবে Result Filter ঠিকই এক্সিকিউট হয় (যা আমরা পরের লেকচারে দেখব)।
🔍 Comprehensive Breakdown
১. The Concept of Short-Circuiting [Priority: 10/10]
The “Why”: রিয়েল-লাইফ প্রজেক্টে, বিশেষ করে ফর্ম সাবমিট করার সময় (যেমন: Create বা Edit), আমরা প্রায়ই চেক করি ইউজার সব ইনপুট ঠিকঠাক দিয়েছে কি না (ModelState.IsValid)। যদি ইনপুট ভুল হয়, তবে আমরা ডাটাবেস থেকে ড্রপডাউনের ভ্যালুগুলো (যেমন: Countries) আবার এনে ইউজারকে ভিউ-টি রিটার্ন করি।
এই কাজটি যদি আমরা CreateHttpPost এবং EditHttpPost—উভয় মেথডের ভেতরেই লিখি, তবে কোড ডুপ্লিকেট হয় (Code duplication)। এই ডুপ্লিকেশন দূর করার জন্য আমরা চেকটি Action Filter-এ নিয়ে যাই। যদি ফিল্টার দেখে যে ডেটা ইনভ্যালিড, তবে সে রিকোয়েস্টটিকে Action Method-এ না পাঠিয়ে সরাসরি ওখান থেকেই View রিটার্ন করে দেয়। একেই বলে Short-circuiting বা স্কিপিং (Skipping)।
২. Creating the Filter and Refactoring Controller [Priority: 10/10]
লেকচারার PersonCreateAndEditPostActionFilter নামে একটি অ্যাসিঙ্ক্রোনাস ফিল্টার তৈরি করেছেন।
প্রথম কাজ (Parameter Renaming):
যেহেতু আমরা ফিল্টারটিকে Create এবং Edit উভয়ের জন্য ব্যবহার করব, তাই Controller-এ তাদের প্যারামিটারের নামগুলো একই রাখতে হবে।
CreateHttpPost-এর প্যারামিটারpersonAddRequest-কে রিনেম করেpersonRequestকরা হলো।EditHttpPost-এর প্যারামিটারpersonUpdateRequest-কেও রিনেম করেpersonRequestকরা হলো।
৩. Code Implementation: The Short-Circuit Logic [Priority: 10/10]
নিচে ফিল্টারটির পূর্ণাঙ্গ কোড ধাপে ধাপে বোঝানো হলো:
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc;
public class PersonCreateAndEditPostActionFilter : IAsyncActionFilter
{
private readonly ICountriesService _countriesService;
// ১. Constructor Injection (Dropdown-এর জন্য CountriesService ইনজেক্ট করা হচ্ছে)
public PersonCreateAndEditPostActionFilter(ICountriesService countriesService)
{
_countriesService = countriesService;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// ২. Controller-কে PersonController-এ টাইপকাস্ট করা
if (context.Controller is PersonsController personsController)
{
// ৩. Model State চেক করা (ভ্যালিডেশন এরর আছে কিনা)
if (!personsController.ModelState.IsValid)
{
// ---------------- SHORT-CIRCUIT LOGIC ----------------
// ড্রপডাউনের জন্য Countries আনা হচ্ছে
var countries = await _countriesService.GetAllCountries();
personsController.ViewBag.Countries = countries.Select(temp =>
new SelectListItem() { Text = temp.CountryName, Value = temp.CountryID.ToString() });
// ViewBag-এ এরর মেসেজ অ্যাড করা হচ্ছে
personsController.ViewBag.Errors = personsController.ModelState.Values
.SelectMany(v => v.Errors)
.Select(e => e.ErrorMessage).ToList();
// 💡 Action Parameter-এর ভ্যালু রিড করা (যাতে ইউজারের দেওয়া ডেটাগুলো ভিউতে ধরে রাখা যায়)
var personRequest = context.ActionArguments["personRequest"];
// 🚀 핵심: context.Result-এ ভ্যালু অ্যাসাইন করা মানেই শর্ট-সার্কিট!
context.Result = personsController.View(personRequest);
// 🛑 CRITICAL: await next() কল করা হচ্ছে না!
return;
}
else
{
// ---------------- NORMAL EXECUTION ----------------
// Model State ভ্যালিড হলে Action Method-কে কল করা হবে
await next();
}
}
else
{
// Controller যদি PersonsController না হয়, তবে নরমাল ফ্লো কন্টিনিউ হবে
await next();
}
}
}
৪. Controller-এর কোড ক্লিনআপ [Priority: 9/10]
যেহেতু শর্ট-সার্কিট লজিকটি ফিল্টার হ্যান্ডেল করছে, তাই এখন Controller-এর CreateHttpPost এবং EditHttpPost মেথড থেকে if (!ModelState.IsValid)-এর পুরো ব্লকটি ডিলিট করে দেওয়া যায়।
এমনকি Action Method-এর শুরুতে আর if কন্ডিশন দেওয়ারও দরকার নেই। কারণ, ফিল্টার পার হয়ে রিকোয়েস্টটি Action Method পর্যন্ত আসার মানেই হলো ModelState ১০০% ভ্যালিড!
এর ফলে Controller-এর কোড অনেক বেশি ক্লিন এবং ফোকাসড হয়ে যায়।
৫. Debugging the Process [Priority: 8/10]
লেকচারার ক্লায়েন্ট-সাইড ভ্যালিডেশন (JavaScript/jQuery) সাময়িকভাবে অফ করে ব্রেকপয়েন্ট বসিয়ে পুরো ফ্লো দেখিয়েছেন।
- যখন ইউজার খালি ফর্ম সাবমিট করে, তখন রিকোয়েস্ট ফিল্টারে আসে।
- ফিল্টার দেখে
ModelState.IsValid == false। - ফিল্টার ভিউ জেনারেট করে
context.Result-এ সেট করে দেয়। - ফলে কন্ট্রোল আর Action Method-এ যায় না, সরাসরি ফিল্টার থেকে রেসপন্স ব্রাউজারে চলে যায়।
🚀 Best Practices & .NET Modern Updates
Best Practices for Short-Circuiting:
- Always Return immediately: যখনই আপনি শর্ট-সার্কিট করবেন (
context.Result = ...), এরপর অবশ্যইreturn;ব্যবহার করে মেথড থেকে বেরিয়ে আসবেন, যাতে ভুল করেও নিচের লাইনে থাকাawait next();কল না হয়ে যায়। - Use Strong Typing for Controller Check: লেকচারে
is PersonsControllerব্যবহার করা হয়েছে, যা ভালো। তবে আরও আধুনিক C# প্যাটার্ন ম্যাচিং ব্যবহার করতে পারেন:if (context.Controller is PersonsController personsController) { ... }(যা লেকচারারও কিছুটা ব্যবহার করেছেন)।
.NET 10 & API Context (The Minimal API Way):
যদিও এই লেকচারটি MVC ভিউ রিটার্ন করার উপর ফোকাস করছে, তবে আপনি যদি .NET 10-এ Web API বা Minimal API তৈরি করেন, তবে শর্ট-সার্কিট করাটা আরও সহজ। সেখানে View রিটার্ন করার বদলে আপনি সরাসরি HTTP Status Code রিটার্ন করতে পারেন।
Minimal API Validation Filter Example (.NET 10):
app.MapPost("/api/persons", (PersonAddRequest request) =>
{
// This action only runs if validation passes
return Results.Ok("Person created successfully.");
})
.AddEndpointFilter(async (context, next) =>
{
var personRequest = context.GetArgument<PersonAddRequest>(0);
// Custom validation logic (or using FluentValidation)
if (string.IsNullOrEmpty(personRequest.PersonName))
{
// 🚀 Short-circuiting the Minimal API way!
return Results.BadRequest("PersonName is required.");
}
// Normal execution
return await next(context);
});
এই প্যাটার্নটি এপিআই ডেভেলপমেন্টের জন্য ইন্ডাস্ট্রিতে সবচেয়ে বেশি ব্যবহৃত হয়।
আশা করি কনসেপ্টটি আপনার কাছে ক্লিয়ার হয়েছে! পরবর্তী লেকচারে আমরা Result Filter নিয়ে বিস্তারিত জানব।