
خاصة لمحبي الكتب من سلسلة "C ++ في غضون 24 ساعة" ، قررت أن أكتب مقالًا حول ASP.NET Core.
إذا لم تكن قد طورت مسبقًا من أجل .NET أو لبعض الأنظمة الأساسية المشابهة ، فلا معنى لذلك أن تخفض من قواك. ولكن إذا كنت مهتمًا بمعرفة ما هو IoC و DI و DIP و Interseptors و Middleware و Filters (أي كل شيء يختلف عن Core من .NET الكلاسيكية) ، فأنت بحاجة بالتأكيد إلى النقر فوق "قراءة المزيد" ، أثناء تطوير من دون فهم كل هذا ، من الواضح أنه غير صحيح.
IOC ، DI ، DIP
إذا بدأ المسرح مع شماعات ، ثم يبدأ ASP.NET Core مع Dependency Injection. من أجل التعامل مع DI ، تحتاج إلى فهم ما هو IoC.
عند الحديث عن IoC ، غالبًا ما يتذكر المرء مبدأ هوليوود وهو "لا تتصل بنا ، وسنتصل بك". مما يعني "لا حاجة للاتصال بنا ، وسوف نتصل بك أنفسنا".
توفر المصادر المختلفة أنماطًا مختلفة يمكن تطبيق IoC عليها. وعلى الأرجح أنهم جميعا على حق ويكملون بعضهم البعض. فيما يلي بعض هذه الأنماط: المصنع ، محدد موقع الخدمة ، طريقة القالب ، المراقب ، الإستراتيجية.
دعنا ننظر إلى IoC باستخدام تطبيق وحدة تحكم بسيط كمثال.
افترض أن لدينا فئتين بسيطتين تقومان بتطبيق واجهة مع طريقة واحدة:
class ConsoleLayer : ILayer { public void Write(string text) { Console.WriteLine(text); } } class DebugLayer : ILayer { public void Write(string text) { Debug.WriteLine(text); } } interface ILayer { void Write(string text); }
كلاهما يعتمد على التجريد (في هذه الحالة ، تعمل الواجهة كعملية تجريدية).
ودعنا نقول أن لدينا كائن مستوى أعلى باستخدام هذه الفئات:
class Logging : ILayer { private ILayer _instance; public Logging(int i) { if (i == 1) { _instance = new ConsoleLayer(); } else { _instance = new DebugLayer(); } } public void Write(string text) { _instance.Write(text); } }
وفقًا لمعلمة المنشئ ، تتم تهيئة متغير _instance بواسطة فئة معينة. حسنًا ، علاوة على ذلك ، عند الاتصال بالكتابة ، سيتم إكمال الإخراج إلى وحدة التحكم أو Debug. يبدو أن كل شيء جيد جدًا وحتى ، كما يبدو ، يتوافق مع الجزء الأول من مبدأ انعكاس التبعية
الكائنات ذات المستوى الأعلى مستقلة عن الكائنات ذات المستوى الأدنى. كل من هؤلاء وتلك تعتمد على التجريد.
في حالتنا ، يعمل ILayer بمثابة تجريد.
ولكن يجب أن يكون لدينا أيضًا كائن بمستوى أعلى. واحد يستخدم فئة التسجيل
static void Main(string[] args) { var log = new Logging(1); log.Write("Hello!"); Console.Read(); }
من خلال تهيئة التسجيل بـ 1 ، ندخل في فئة التسجيل مثيلًا للفئة التي تُخرج البيانات إلى وحدة التحكم. إذا تم تهيئة التسجيل باستخدام أي رقم آخر ، فسوف يقوم log.Write بإخراج البيانات إلى تصحيح الأخطاء. كل شيء ، على ما يبدو ، يعمل ، لكنه يعمل بشكل سيء. يعتمد كائننا ذي المستوى الأعلى على تفاصيل رمز الكائن ذي المستوى الأدنى - فئة التسجيل. إذا قمنا بتغيير شيء ما في هذه الفئة ، فسنحتاج إلى تغيير رمز الفئة الرئيسية. لمنع حدوث ذلك ، سنقوم بانعكاس التحكم - انعكاس التحكم. لنجعل الفئة الرئيسية تتحكم في ما يحدث في فئة التسجيل. ستتلقى فئة التسجيل ، كمعلمة مُنشئ ، مثيل لفئة تنفذ واجهة ILayer
class Logging { private ILayer _instance; public Logging(ILayer instance) { _instance = instance; } public void Write(string text) { _instance.Write(text); } }
والآن ، سيبدو صفنا الرئيسي كما يلي:
static void Main(string[] args) { var log = new Logging(new DebugLayer()); log.Write("Hello!"); Console.Read(); }
في الواقع ، نحن تزيين كائن تسجيل لدينا مع الكائن الضروري بالنسبة لنا.
يتوافق الآن طلبنا مع الجزء الثاني من مبدأ انعكاس التبعية:
التجريدات مستقلة عن التفاصيل. التفاصيل تعتمد على التجريد. أي لا نعرف تفاصيل ما يحدث في فئة التسجيل ، فنحن نمرر الفصل الذي ينفذ التجريد الضروري.
هناك مثل هذا المصطلح اقتران ضيق - اتصال ضيق. كلما كان الاتصال بين المكونات في التطبيق أضعف ، كان ذلك أفضل. أود أن أشير إلى أن هذا المثال للتطبيق البسيط لا يصل إلى المستوى المثالي قليلاً. لماذا؟ نعم ، لأنه في أعلى مستوى في Main ، نستخدم مرتين إنشاء مثيلات فئة باستخدام new. وهناك عبارة ذاكري "New is a clue" - مما يعني أنه كلما قل استخدامك الجديد ، قلت اتصالات المكونات في التطبيق بشكل أفضل. من الناحية المثالية ، يجب ألا نستخدم DebugLayer الجديد ، ولكن يجب أن نحصل على DebugLayer بطريقة أخرى. أي واحد؟ على سبيل المثال ، من حاوية IoC أو باستخدام انعكاس من معلمة تم تمريرها إلى Main.
لقد اكتشفنا الآن ما هو انعكاس التحكم (IoC) وما هو انعكاس التبعية (DIP). يبقى أن نفهم ما هو Dependency Injection (DI). IoC هو نموذج التصميم. Dependency Injection هو النمط. هذا ما لدينا الآن في مُنشئ فئة التسجيل. نحصل على مثيل تبعية محددة. تعتمد فئة التسجيل على مثيل لفئة تقوم بتنفيذ ILayer. ويتم حقن هذه الحالة من خلال المنشئ.
حاوية IoC
حاوية IoC هي كائن يحتوي على العديد من التبعيات المحددة (التبعية). يمكن أن يُطلق على التبعية خدمة - كقاعدة عامة ، إنها فئة ذات وظيفة معينة. إذا لزم الأمر ، يمكن الحصول على اعتماد النوع المطلوب من الحاوية. حقن التبعية في حاوية هو حقن. استخراج - حل. فيما يلي مثال على أبسط حاوية IoC مكتوبة ذاتيًا:
public static class IoCContainer { private static readonly Dictionary<Type, Type> _registeredObjects = new Dictionary<Type, Type>(); public static dynamic Resolve<TKey>() { return Activator.CreateInstance(_registeredObjects[typeof(TKey)]); } public static void Register<TKey, TConcrete>() where TConcrete : TKey { _registeredObjects[typeof(TKey)] = typeof(TConcrete); } }
فقط عشرة أسطر من التعليمات البرمجية ، لكن يمكنك استخدامها بالفعل (ليس للإنتاج ، بالطبع ، ولكن للأغراض التعليمية).
يمكنك تسجيل التبعية (على سبيل المثال ، ConsoleLayer أو DebugLayer التي استخدمناها في المثال السابق) مثل هذا:
IoCContainer.Register<ILayer, ConsoleLayer>();
واستخرجها من الحاوية في المكان المناسب للبرنامج مثل هذا:
ILayer layer = IoCContainer.Resolve<ILayer>(); layer.Write("Hello from IoC!");
في الحاويات الحقيقية ، يتم أيضًا تطبيق Dispos () ، مما يسمح لك بتدمير الموارد التي أصبحت غير ضرورية.
بالمناسبة ، اسم حاوية IoC لا ينقل المعنى بالضبط ، لأن مصطلح IoC أوسع بكثير في التطبيق. لذلك ، تم استخدام مصطلح حاوية DI مؤخرًا في كثير من الأحيان (بما أن حقن التبعية لا يزال مطبقًا).
عمر الخدمة + طرق تمديد مختلفة في Root Composition
تحتوي تطبيقات ASP.NET Core على ملف Startup.cs ، وهو نقطة الانطلاق للتطبيق لتكوين DI. يقوم بتكوين DI في أسلوب ConfigureServices.
public void ConfigureServices(IServiceCollection services) { services.AddScoped<ISomeRepository, SomeRepository>(); }
سيضيف هذا الرمز فئة SomeRepository إلى حاوية DI ، والتي تنفذ واجهة ISomeRepository. حقيقة إضافة الخدمة إلى الحاوية باستخدام AddScoped تعني أنه سيتم إنشاء مثيل للفئة في كل مرة يتم فيها طلب صفحة.
يمكنك إضافة خدمة إلى حاوية دون تحديد واجهة.
services.AddScoped<SomeRepository>();
لكن هذه الطريقة غير موصى بها ، حيث يفقد التطبيق مرونته وتظهر الاتصالات الوثيقة. يوصى دائمًا بتحديد واجهة ، لأنه في هذه الحالة ، في أي وقت ، يمكنك استبدال تطبيق واجهة أخرى. وإذا كانت التطبيقات تدعم مبدأ استبدال Liskov ، فعن طريق تغيير اسم فئة التنفيذ بـ "نقرة من المعصم" ، ستقوم بتغيير وظيفة التطبيق بأكمله.
هناك خياران إضافيان لإضافة خدمة - AddSingleton و AddTransient.
عند استخدام AddSingleton ، يتم إنشاء الخدمة مرة واحدة ، وعند استخدام التطبيق ، تنتقل المكالمة إلى نفس المثيل. استخدم هذه الطريقة بعناية خاصة ، حيث يمكن حدوث تسرب للذاكرة ومشاكل تعدد مؤشرات الترابط.
AddSingleton لديه ميزة صغيرة. يمكن تهيئته إما عند أول وصول إليه
services.AddSingleton<IYourService, YourService>();
إما على الفور عند إضافتها إلى المنشئ
services.AddSingleton<IYourService>(new YourService(param));
في الطريقة الثانية ، يمكنك حتى إضافة معلمة إلى المنشئ.
إذا كنت ترغب في إضافة معلمة إلى مُنشئ الخدمة المضافة ليس فقط باستخدام AddSingleton ، ولكن أيضًا باستخدام AddTransient / AddScoped ، يمكنك استخدام تعبير lambda:
services.AddTransient<IYourService>(o => new YourService(param));
وأخيرًا ، عند استخدام AddTransient ، يتم إنشاء خدمة في كل مرة تدخل فيها. كبيرة للخدمات خفيفة الوزن التي لا تستهلك الذاكرة والموارد.
إذا كان مع كل من AddSingleton و AddScoped يجب أن يكون كل شيء أكثر أو أقل وضوحًا ، فإن AddTransient يحتاج إلى توضيح. تقدم الوثائق الرسمية مثالًا يتم فيه إضافة خدمة معينة إلى حاوية DI كمعلمة لمنشئ خدمة أخرى وبشكل منفصل بشكل مستقل. وفي حالة إضافته بشكل منفصل باستخدام AddTransient ، فإنه ينشئ مثيله مرتين. سأقدم مثالًا بسيطًا للغاية. في الحياة الحقيقية ، لا ينصح للاستخدام ، لأنه فئات للبساطة لا ترث واجهات. دعنا نقول لدينا فئة بسيطة:
public class Operation { public Guid OperationId { get; private set; } public Operation() { OperationId = Guid.NewGuid(); } }
وهناك فئة ثانية تحتوي على الخدمة الأولى كخدمة تابعة وتتلقى هذه التبعية كمعلمة مُنشئ:
public class OperationService { public Operation Operation { get; } public OperationService (Operation operation) { Operation = operation; } }
الآن نحقن خدمتين:
services.AddTransient<Operation>(); services.AddScoped<OperationService>();
وفي بعض وحدات التحكم في العمل ، أضف إيصال تبعياتنا واعرض القيم في نافذة التصحيح.
public IActionResult Index([FromServices] Operation operation, [FromServices] OperationService operationService) { Debug.WriteLine(operation.OperationId); Debug.WriteLine(operationService.Operation.OperationId); return View(); }
لذلك ، نتيجة لذلك ، نحصل على قيمتين مختلفتين لـ Guid. ولكن إذا استبدلنا AddTransient بـ AddScoped ، فنتيجة لذلك نحصل على قيمتين متطابقتين.
تحتوي حاوية IoC للتطبيق ASP.NET Core على بعض الخدمات بشكل افتراضي. على سبيل المثال ، IConfiguration هي خدمة يمكنك من خلالها الحصول على إعدادات التطبيق من ملفات appsettings.json و appsettings.Development.json. IHostingEnvironment و ILoggerFactory التي يمكنك من خلالها الحصول على التكوين الحالي وفئة المساعد التي تتيح التسجيل.
يتم استرداد الفئات من الحاوية باستخدام البناء النموذجي التالي (المثال الأكثر شيوعًا):
private readonly IConfiguration _configuration; public SomePageController(IConfiguration configuration) { _configuration = configuration; } public async Task<IActionResult> Index() { string connectionString = _configuration["connectionString"]; }
يتم إنشاء متغير بمعدلات وصول خاصة للقراءة فقط في نطاق وحدة التحكم. يتم الحصول على التبعية من الحاوية في مُنشئ الفئة وتعيين متغير خاص. علاوة على ذلك يمكن استخدام هذا المتغير في أي أساليب أو تحكم العمل.
في بعض الأحيان لا ترغب في إنشاء متغير من أجل استخدامه في إجراء واحد فقط. ثم يمكنك استخدام السمة [FromServices]. مثال:
public IActionResult About([FromServices] IDateTime dateTime) { ViewData["Message"] = « " + dateTime.Now; return View(); }
يبدو غريباً ، ولكن حتى لا يتم استدعاء طريقة الفئة الثابتة DateTime.Now () في الكود ، يتم ذلك أحيانًا حتى يتم الحصول على قيمة الوقت من الخدمة كمعلمة. وبالتالي ، يصبح من الممكن تمرير أي وقت كمعلمة ، مما يعني أنه من الأسهل لكتابة الاختبارات ، وكقاعدة عامة ، يصبح من السهل إجراء تغييرات على التطبيق.
هذا لا يعني أن ساكنة هو الشر. طرق ثابتة هي أسرع. وعلى الأرجح يمكن استخدام ساكنة في مكان ما في حاوية IoC نفسها. ولكن إذا حفظنا طلبنا من كل شيء ثابت وجديد ، فسنحصل على المزيد من المرونة.
حاويات DI الطرف الثالث
ما نظرنا إليه وما تقوم به حاوية ASP.NET Core DI بشكل افتراضي هو حقن المنشئ. لا تزال هناك فرصة لحقن التبعية في الخاصية باستخدام حقن الخاصية ما يسمى ، ولكن هذه الميزة غير متوفرة في الحاوية المضمنة في ASP.NET Core. على سبيل المثال ، قد يكون لدينا فئة تقوم بتطبيقها كتبعية ، وهذه الفئة لها نوع من الملكية العامة. الآن تخيل أننا أثناء أو بعد حقن التبعية ، نحتاج إلى تعيين قيمة الخاصية. دعنا نعود إلى مثال مشابه للمثال الذي درسناه مؤخرًا.
إذا كان لدينا مثل هذه الفئة:
public class Operation { public Guid OperationId { get; set; } public Operation() {} }
والتي يمكننا أن نقدم إدمان ،
services.AddTransient<Operation>();
ثم باستخدام الحاوية القياسية ، لا يمكننا ضبط قيمة العقار.
إذا كنت تريد استخدام هذه الفرصة لتعيين قيمة لخاصية OperationId ، فيمكنك استخدام نوع من حاوية DI لجهة خارجية تدعم خاصية الحقن. بالمناسبة ، لا ينصح حقن الممتلكات بشكل خاص. ومع ذلك ، لا يزال هناك أسلوب حقن وحقن طريقة Setter ، والتي قد تكون في متناول يديك والتي لا تدعمها الحاوية القياسية أيضًا.
قد تحتوي حاويات الجهات الخارجية على ميزات أخرى مفيدة للغاية. على سبيل المثال ، باستخدام حاوية تابعة لجهة خارجية ، يمكنك فقط إضافة التبعية إلى وحدات التحكم التي تحتوي على كلمة معينة في الاسم. وغالبا ما تستخدم حالة - حاويات DI ، الأمثل للأداء.
فيما يلي قائمة ببعض حاويات DI التابعة لجهة خارجية والتي يدعمها ASP.NET Core: Autofac ، Castle Windsor ، LightInject ، DryIoC ، StructureMap ، Unity
على الرغم من استخدام حاوية DI قياسية ، لا يمكنك استخدام حقن الخاصية / الطريقة ، ولكن يمكنك تنفيذ خدمة تابعة كمعلمة مُنشئ عن طريق تطبيق نمط المصنع على النحو التالي:
services.AddTransient<IDataService, DataService>((dsvc) => { IOtherService svc = dsvc.GetService<IOtherService>(); return new DataService(svc); });
في هذه الحالة ، سيعود GetService فارغًا إذا لم يتم العثور على الخدمة التابعة. يوجد تباين في GetRequiredService يؤدي إلى استثناء إذا لم يتم العثور على الخدمة التابعة.
تطبق عملية الحصول على خدمة تابعة باستخدام GetService بالفعل نمط محدد موقع الخدمة.
Autofac
دعونا نلقي نظرة على Autofac مع مثال عملي. بسهولة ، يمكن تسجيل الخدمات الواردة من الحاوية واستلامها ، سواء بالطريقة الافتراضية أو باستخدام Autofac.
تثبيت حزمة NuGet Autofac.Extensions.DependencyInjection.
تغيير القيمة التي تم إرجاعها بواسطة الأسلوب ConfigureServices من باطلة إلى IServiceProvider. وإضافة الممتلكات
public IContainer ApplicationContainer { get; private set; }
بعد ذلك ، سيكون من الممكن إضافة رمز مثل التالي إلى نهاية طريقة ConfigureServices لفئة بدء التشغيل (هذا مجرد خيار من خيارات تسجيل الخدمات):
services.AddTransient<ISomeRepository, SomeRepository>(); var builder = new ContainerBuilder(); builder.Populate(services); builder.RegisterType<AnotherRepository>().As<IAnotherRepository>(); this.ApplicationContainer = builder.Build(); return new AutofacServiceProvider(this.ApplicationContainer);
هنا builder.Populate (الخدمات) ؛ يضيف خدمات من IServiceCollection إلى الحاوية. إضافة إلى أنه من الممكن بالفعل تسجيل الخدمات مع builder.RegisterType. اوه نعم لقد نسيت تقريبا. يجب عليك تغيير قيمة الإرجاع للأسلوب ConfigureServices من الفراغ إلى IServiceProvider.
AOP مع ASP.NET Core - Autofac Interseptors
يتحدثون عن البرمجة الموجهة نحو الجوانب ، ويذكرون مصطلحًا آخر - المخاوف الشاملة. القلق هو بعض المعلومات التي تؤثر على الكود. في النسخة الروسية يستخدمون كلمة المسؤولية. حسنًا ، المخاوف الشاملة هي مسؤوليات تؤثر على المسؤوليات الأخرى. لكن من الناحية المثالية ، ينبغي ألا تؤثر على بعضها البعض ، أليس كذلك؟ عندما تؤثر على بعضها البعض ، يصبح من الصعب تغيير البرنامج. سيكون أكثر ملاءمة عندما يكون لدينا جميع العمليات بشكل منفصل. يمكن إجراء التسجيل والمعاملات والتخزين المؤقت وغير ذلك الكثير باستخدام AOP دون تغيير رمز الفئات والأساليب نفسها.
في عالم .NET ، يتم استخدام طريقة غالبًا عندما يتم تضمين كود AOP باستخدام معالج النشر في رمز تطبيق مترجم بالفعل ( PostSharp ) ، أو بدلاً من ذلك ، يمكنك استخدام interceptors - فهذه هي روابط الأحداث التي يمكن إضافتها إلى رمز التطبيق. يستخدم هؤلاء المعترضون ، كقاعدة عامة ، الديكور الذي درسناه بالفعل لعملهم.
دعونا إنشاء اعتراضية الخاصة بك. المثال الأبسط والأكثر نموذجية والذي يسهل إعادة إنتاجه هو التسجيل.
بالإضافة إلى حزمة Autofac.Extensions.DependencyInjection ، سنقوم أيضًا بتثبيت حزمة Autofac.Extras.DynamicProxy
مثبتة؟ أضف فئة سجل بسيطة سيتم استدعاؤها عند الوصول إلى خدمات معينة.
public class Logger : IInterceptor { public void Intercept(IInvocation invocation) { Debug.WriteLine($"Calling {invocation.Method.Name} from Proxy"); invocation.Proceed(); } }
أضف إلى تسجيلنا Autofac تسجيل المعترض:
builder.Register(i => new Logger()); builder.RegisterType<SomeRepository >() .As<ISomeRepository >() .EnableInterfaceInterceptors() .InterceptedBy(typeof(Logger));
والآن ، مع كل مكالمة إلى الفصل ، سيتم استدعاء طريقة اعتراض فئة المسجل.
وبالتالي ، يمكننا تبسيط حياتنا وعدم كتابة إدخال سجل في بداية كل طريقة. سيكون لدينا تلقائيا. وإذا رغبت في ذلك ، سيكون من السهل علينا تغييره أو تعطيله للتطبيق بأكمله.
يمكننا أيضًا إزالة .InterceptedBy (typeof (Logger)) ؛ وقم بإضافة اعتراض الاتصال فقط لخدمات تطبيقات محددة باستخدام السمة [Intercept (typeof (Logger))] - يجب عليك تحديد ذلك قبل رأس الفصل.
الوسيطة
يحتوي ASP.NET على سلسلة معينة من مكالمات التعليمات البرمجية التي تحدث عند كل طلب. حتى قبل تحميل UI / MVC ، يتم تنفيذ بعض الإجراءات.
هذا ، على سبيل المثال ، إذا أضفنا في بداية طريقة تكوين فئة Startup.cs الكود
app.Use(async (context, next) => { Debug.WriteLine(context.Request.Path); await next.Invoke(); });
ثم يمكننا أن نرى في وحدة التحكم تصحيح الملفات ما طلبات طلباتنا. في الواقع ، نحصل على قدرات AOP "خارج الصندوق"
مثال عديم الفائدة ، ولكنه واضح وغني بالمعلومات حول استخدام البرامج الوسيطة ، سأريك الآن:
public void Configure(IApplicationBuilder app) { app.Use(async (context, next) => { await context.Response.WriteAsync("Hello!" + Environment.NewLine); await next.Invoke(); }); app.Run(async context => { await context.Response.WriteAsync("Hello again."); }); }
مع كل طلب ، تبدأ سلسلة من المكالمات. من كل app.Use ، بعد الاتصال next.invoke () ، يتم الانتقال إلى المكالمة التالية. وكل شيء ينتهي بعد أعمال التطبيق.
يمكنك تنفيذ بعض التعليمات البرمجية فقط عند الوصول إلى مسار معين.
يمكنك القيام بذلك باستخدام app.Map:
private static void Goodbye(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Goodbye!"); }); } public void Configure(IApplicationBuilder app) { app.Map("/goodbye", Goodbye); app.Run(async context => { await context.Response.WriteAsync("Hello!"); }); }
الآن ، إذا انتقلت إلى صفحة الموقع ، يمكنك رؤية النص "Hello!" ، وإذا أضفت / وداعًا إلى شريط العناوين ، فسترى وداعًا.
بالإضافة إلى "الاستخدام" و "الخريطة" ، يمكنك استخدام "UseWhen" أو "MapWhen" لإضافة رمز إلى سلسلة البرامج الوسيطة فقط في ظل ظروف معينة محددة.
حتى الآن لا تزال هناك أمثلة عديمة الفائدة ، أليس كذلك؟ هنا مثال طبيعي:
app.Use(async (context, next) => { context.Response.Headers.Add("X-Frame-Options", "DENY"); context.Response.Headers.Add("X-Content-Type-Options", "nosniff"); context.Response.Headers.Add("X-Xss-Protection", "1"); await next(); });
نضيف هنا رؤوس لكل طلب للمساعدة في حماية الصفحة من هجمات القراصنة.
أو هنا مثال على الترجمة:
var supportedCultures = new[] { new CultureInfo("ru"), new CultureInfo("fr") }; app.UseRequestLocalization(new RequestLocalizationOptions { DefaultRequestCulture = new RequestCulture("ru"), SupportedCultures = supportedCultures, SupportedUICultures = supportedCultures });
الآن إذا أضفت المعلمة؟ Culture = fr إلى عنوان الصفحة ، فيمكنك تبديل لغة التطبيق إلى الفرنسية (إذا تمت إضافة الترجمة إلى التطبيق الخاص بك ، فسيعمل كل شيء)
مرشحات
إذا كانت سلسلة البرامج الوسيطة تشير إلى العمليات قبل MVC ، فستعمل المرشحات مع MVC.
يوضح المخطط التخطيطي التالي كيفية عمل المرشحات.

أولاً ، تعمل عوامل تصفية التراخيص. أي يمكنك إنشاء نوع من الفلاتر أو عدة فلاتر وإدخال فيها نوعًا من كود التفويض الذي سينجح بناءً على الطلبات.
ثم تفي مرشحات الموارد. باستخدام عوامل التصفية هذه ، يمكنك ، على سبيل المثال ، إرجاع بعض المعلومات من ذاكرة التخزين المؤقت.
ثم يحدث ربط البيانات ويتم تنفيذ عوامل تصفية الإجراء. من خلال مساعدتهم ، يمكنك معالجة المعاملات التي تم تمريرها إلى Action والنتيجة التي تم إرجاعها.
مرشحات الاستثناء لأن تلميحات الاسم تتيح لك إضافة نوع من معالجة الأخطاء العامة للتطبيق. يجب أن تكون مريحة جدًا للتعامل مع الأخطاء في كل مكان. وهناك نوع من AOP-shny زائد.
تتيح لك عوامل تصفية النتيجة تنفيذ بعض الإجراءات قبل تنفيذ وحدة التحكم في الإجراء أو بعده. تشبه عوامل تصفية الإجراء تمامًا ، لكن يتم تنفيذها فقط في حالة عدم وجود أخطاء. مناسبة للمنطق مرتبطة عرض.
. :
public class YourCustomFilter : Attribute, IAuthorizationFilter { public async void OnAuthorization(AuthorizationFilterContext context) { // - , , context.Result = new ContentResult() { Content = " " }; } }
DI ( Startup.cs)
services.AddScoped<YourCustomFilter>();
- Action
[ServiceFilter(typeof(YourCustomFilter))]
– middleware - action . Configure
public class MyMiddlewareFilter { public void Configure(IApplicationBuilder applicationBuilder) { applicationBuilder.Use(async (context, next) => { Debug.WriteLine(" middleware!"); await next.Invoke(); }); } }
Action-
[MiddlewareFilter(typeof(MyMiddlewareFilter))]