স্বাগতম! আমরা এখন আপনার কোর্সের Section 20 (Filters)-এর “Global Filters & Order of Execution” (লেকচার ২৮৯) টপিকে আছি।
আগের লেকচারে আমরা Action Method-এর উপরে (Method level) ফিল্টার ব্যবহার করা শিখেছিলাম। কিন্তু রিয়েল-লাইফ প্রোজেক্টে এমন অনেক লজিক থাকে (যেমন: গ্লোবাল এরর হ্যান্ডলিং বা সিকিউরিটি হেডার) যা পুরো প্রোজেক্টের প্রত্যেকটি রিকোয়েস্টে রান করা প্রয়োজন। আজ আমরা শিখব ফিল্টার কতভাবে বা কতটি স্কোপে (Scope) অ্যাপ্লাই করা যায় এবং তারা কোন সিরিয়ালে রান করে।
📝 Quick Revision Summary
- ৩টি Scope: Filter ৩টি স্তরে অ্যাপ্লাই করা যায়—
- Method Level (শুধু নির্দিষ্ট একটি Action-এর জন্য)
- Controller Level (ওই Controller-এর সব Action-এর জন্য)
- Global Level (পুরো প্রোজেক্টের সব Controller ও Action-এর জন্য)
-
Global Filter Setup:
Program.csফাইলেAddControllersWithViews()-এর ভেতরেoptions.Filters.Add()ব্যবহার করে গ্লোবাল ফিল্টার সেট করতে হয়। -
Dependency Injection in Program.cs: গ্লোবাল ফিল্টারে ম্যানুয়ালি ஆর্গুমেন্ট পাঠানোর সময়
Builder.Services.BuildServiceProvider()ব্যবহার করে DI কন্টেইনার থেকে সার্ভিস (যেমনILogger) নিতে হয়। -
Execution Order (Default):
-
OnActionExecuting(Before Method): Global -> Controller -> Method (Broader to Narrow) -
OnActionExecuted(After Method): Method -> Controller -> Global (Narrow to Broader) -
The Wrapping Concept: সবসময় Broader scope (Global), Narrow scope-কে (Method) চারপাশ থেকে ঘিরে (wrap করে) রাখে।
🔍 Comprehensive Breakdown
১. Scope of Filters [Priority: 10/10]
Filter-কে আমরা তিনটি জায়গায় বসাতে পারি:
- Method Level: আগের লেকচারে আমরা যেমনটা দেখেছি, শুধু
Indexঅ্যাকশনের উপরে[TypeFilter(...)]বসালে এটি শুধুIndex-এর জন্যই কাজ করবে। - Controller Level: যদি
[TypeFilter(...)]অ্যাকশনের উপরে না বসিয়ে সরাসরি Controller ক্লাসের (যেমনPersonsController) উপরে বসানো হয়, তবে ওই কন্ট্রোলারের ভেতরে থাকাIndex,Create,Edit—সবগুলো অ্যাকশনের জন্যই ফিল্টারটি কাজ করবে। - Global Level: যদি আপনি চান আপনার ফিল্টারটি প্রোজেক্টের সমস্ত কন্ট্রোলার এবং সমস্ত অ্যাকশনের জন্য কাজ করুক, তবে সেটিকে গ্লোবাল ফিল্টার হিসেবে রেজিস্টার করতে হবে।
২. Global Filter Registration (Program.cs) [Priority: 10/10]
গ্লোবাল ফিল্টার সাধারণত অ্যাপ্লিকেশনের স্টার্টআপ বা Program.cs ফাইলে কনফিগার করা হয়।
Generic Way (বিনা আর্গুমেন্টে): যদি ফিল্টারে শুধু Dependency Injection (DI) থাকে, কোনো এক্সট্রা আর্গুমেন্ট না থাকে, তবে এভাবে রেজিস্টার করা যায়:
builder.Services.AddControllersWithViews(options => {
options.Filters.Add<ResponseHeaderActionFilter>();
});
Non-Generic Way (আর্গুমেন্ট সহকারে):
আমাদের ResponseHeaderActionFilter-এ ILogger, key এবং value দরকার। গ্লোবাল লেভেলে আর্গুমেন্ট পাস করতে হলে আমাদের ফিল্টারটির অবজেক্ট নিজেদের তৈরি করে দিতে হবে। কিন্তু ILogger-এর অবজেক্ট আমরা নিজে বানাব না, সেটি DI কন্টেইনার থেকে আনব।
// ১. Service Provider তৈরি করা হচ্ছে যাতে DI কন্টেইনার থেকে Service আনা যায়
var serviceProvider = builder.Services.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<ResponseHeaderActionFilter>>();
// ২. Global Filter অ্যাড করা হচ্ছে আর্গুমেন্ট সহ
builder.Services.AddControllersWithViews(options => {
options.Filters.Add(new ResponseHeaderActionFilter(
logger,
"My-Key-From-Global",
"My-Value-From-Global"
));
});
💡 Note: GetRequiredService ব্যবহার করলে সার্ভিস না পেলে অ্যাপ্লিকেশন স্টার্ট হওয়ার সময়ই Error/Exception দেবে। আর GetService ব্যবহার করলে null রিটার্ন করবে। সাধারণত কোর সার্ভিসের ক্ষেত্রে GetRequiredService ব্যবহার করাই বেস্ট প্র্যাকটিস।
৩. Default Order of Execution [Priority: 10/10]
ধরা যাক, আপনি একই ResponseHeaderActionFilter তিনটি জায়গাতেই বসিয়েছেন (Method, Controller এবং Global) এবং তিন জায়গায় ভিন্ন ভিন্ন আর্গুমেন্ট (যেমন: From-Action, From-Controller, From-Global) দিয়েছেন। এখন Index মেথড কল করলে কোন ফিল্টারটি আগে রান করবে?
লেকচারার এই সিকোয়েন্সটিকে একটি “Wrapping” বা “Onion” আর্কিটেকচারের সাথে তুলনা করেছেন।
Action-এর আগে (OnActionExecuting):
ব্রডার (Broader) স্কোপ আগে রান করে, এরপর ন্যারো (Narrow) স্কোপ।
- Global Filter (সবচেয়ে বাইরে)
- Controller Level Filter
- Method Level Filter (সবচেয়ে ভেতরে)
(এরপর মূল Action Method এক্সিকিউট হয়)
Action-এর পরে (OnActionExecuted):
এবার সিরিয়ালটি সম্পূর্ণ উল্টো হয়ে যায়! ঠিক যেমন একটি ঘর থেকে বের হওয়ার সময় ভেতরের দরজা আগে পার হতে হয়।
- Method Level Filter
- Controller Level Filter
- Global Filter
৪. Visualization of Wrapping (The Mental Model) [Priority: 8/10]
এই কনসেপ্টটি বুঝতে লেকচারারের Wrapping আইডিয়াটি মাথায় রাখুন:
[ Global OnActionExecuting ]
⬇
[ Controller OnActionExecuting ]
⬇
[ Method OnActionExecuting ]
⬇
( ACTION METHOD RUNS )
⬇
[ Method OnActionExecuted ]
⬇
[ Controller OnActionExecuted ]
⬇
[ Global OnActionExecuted ]
🚀 Best Practices & .NET Modern Updates
Best Practices for Execution Order:
- Avoid Duplicate Filters: একই কাজের ফিল্টার তিন জায়গায় বসানোটা সাধারণত হয় না (লেকচারে শুধু ডেমোনস্ট্রেশনের জন্য দেখানো হয়েছে)। সাধারণত গ্লোবাল ফিল্টার (যেমন Logging, Error Handling) এবং মেথড ফিল্টার (যেমন স্পেসিফিক ভ্যালিডেশন) আলাদা কাজের জন্য ব্যবহৃত হয়।
- Be Careful with
BuildServiceProvider:Program.cs-এর ভেতরেbuilder.Services.BuildServiceProvider()কল করাটা একটি “Anti-pattern” হিসেবে গণ্য হয়, কারণ এটি মেমরি লিক বা ডুপ্লিকেট সার্ভিস তৈরি করতে পারে (যা “Service Provider Capturing” নামে পরিচিত)।
.NET 10 Modern Code implementation (The Recommended Way for Global Filters):
.NET 10 বা আপডেটেড ভার্সনে DI Service পাওয়ার জন্য BuildServiceProvider() কল করার পরিবর্তে IFilterFactory বা TypeFilterAttribute ব্যবহার করা হয় গ্লোবাল লেভেলেও, যাতে ফ্রেমওয়ার্ক নিজেই লাইফসাইকেল মেইনটেইন করে।
অথবা, যদি আপনি সরাসরি Add করতে চান, তবে TypeFilterAttribute-এর মাধ্যমে করতে পারেন:
builder.Services.AddControllersWithViews(options =>
{
// NO BuildServiceProvider() needed! Framework handles DI automatically.
options.Filters.Add(new TypeFilterAttribute(typeof(ResponseHeaderActionFilter))
{
Arguments = new object[] { "My-Key-From-Global", "My-Value-From-Global" }
});
});
পরবর্তী লেকচারে আমরা দেখব, যদি এই ডিফল্ট অর্ডার (Global -> Controller -> Method) আমাদের পছন্দ না হয়, তবে আমরা কীভাবে Order প্রপার্টি ব্যবহার করে এটি কাস্টমাইজ বা ওভাররাইড করতে পারি।