در این قسمت قصد داریم در رابطه با مکانیزم نگاشت شدن Request های وارد شده به برنامههای Asp.Net Core به response هایی که در پاسخ به آنها به سمت کاربر ارسال میشوند صحبت کنیم. امیدواریم که با Asp.Net Core آشنایی مناسبی داشته باشید. برای این کار می توانید از بسته ی آموزش ویدئویی ساخت وب اپلیکیشن با ASP.NET Core Razor دیدن کنید.
بررسی مکانیزم نگاشت Request ها به Response ها در Asp.Net Core
احتمالاً میدانید که به صورت درونی در Asp.Net Core درخواست ها و یا Request های کاربران به Response ها و یا پاسخهایی نگاشت و یا اصطلاحاً Map میشوند. قصد داریم شما را با این واژگان فنی آشنا کنیم تا رفته رفته بتوانید مطالب منتشر شده در رابطه با این فریم ورک را بهتر درک کنید. در سطوح بسیار پایین این نگاشت توسط middleware ها اتفاق میافتد و بسیاری از اپلیکیشنهای Asp.Net Core و حتی مایکروسرویس ها ممکن است فقط از middleware ها ساخته شده باشند. زمانی که ازMVC Asp.Net Core استفاده میکنید میتوانید در سطوح بالاتری عمل کرده و در رابطه با مفاهیمی از قبیل Route ها و Controller ها و Action ها فکر کنید. هر Request وارد شده به برنامه بر اساس routing table تعریف شده در برنامه به یک Routeنگاشت میشود و سپس بر اساس این نگاشت یک Action Method در یک Controller اجرا شده و Request مورد نظر را Handle می کند. اگر هیچ Route خاصی متناظر با Request وارد شده به برنامه پیدا نشود یک error handler وارد عمل میشود و یک NotFound به عنوان نتیجه کار به سمت کاربر ارسال می کند.
در اپلیکیشن های Asp.Net Core MVC از سه روش conventional routes و attribute routes و یا ترکیبی از هر دو استفاده میکنیم. conventional route ها در واقع در کد تعریف می شوند و با استفاده از برخی از Convention ها و یا رسم و رسوم های مسیریابی و یا همان Routing شبیه به آنچه که در مثال زیر مشاهده می کنید لحاظ می گردند.
کدی که در مثال بالا مشاهده می کنید باعث تعریف شدن یک Route به نام Default در Routing Table برنامه خواهد شد. این route template شامل placeholders هایی با نامهای Controller و Action و ID می باشد. در واقع Controller و Action مکان هایی هستند که در آنها نام Controller ها و Action ها لحاظ می گردند که به صورت پیشفرض به Home و Index تبدیل گردیده اند. ID نیز که به صورت اختیاری و با استفاده از علامت سوال تعریف شده است پارامتر ورودی است که میتواند پس از یک Action Method لحاظ بگردد. بر اساس Convention ها در این قسمت، اولین بخش یک Request به Controller موجود در این Route نگاشت شده و دومین بخش به یک Action و نهایتاً در صورت نیاز سومین بخش به یک ID نگاشت خواهد شد. Conventional route ها اغلب در یک مکان تک از یک برنامه از قبیل Configure method در کلاس Startup تعریف می شود.
اما نوع دیگری از Route ها Attribute Route می باشد که به Controller ها و Action ها به طور مستقیم اعمال می گردند و به صورت سراسری و یا Global تعریف نمیگردند. استفاده کردن از Attribute Route ها نیز مزیت های خاص خود را دارد. برای مثال این نوع از Routing قابلیت مشاهده پذیری بالاتری را دارد و سریعاً در زمان بررسی کردن یک Action Method دیده خواهد شد. البته استفاده از این روش باعث میشود که کدهای مربوط به Routing برنامه در یک مکان تک قرار نگرفته و در بخشهای مختلفی از برنامه لحاظ بشود. با استفاده از Attribute Route ها شما می توانید چندین Route مختلف را برای یک Action Method تک لحاظ کرده و علاوه بر این Route های مختلف را بین Controller ها و Action های مورد نظرتان ترکیب کنید. مثالی از Attribute Route ها را در قسمت زیر مشاهده می کنید.
روت ها می توانند توسط Attribute هایی از قبیل [HttpGet] و یا موارد مشابه لحاظ بشوند. در این صورت دیگر نیازی به استفاده کردن از Attribute خاص Route را نخواهید داشت. در رابطه با Attribute ها توصیه میکنیم از بسته ی آموزش ویدئویی Attribute ها در سی شارپ دیدن کنید. علاوه بر این موضوع Attribute Route ها میتوانند از توکن ها به منظور کاهش دادن کد نوشته شده و جلوگیری کردن از تکرار نام Controller ها و Action ها نیز بهرهمند شوند. این موضوع در کد زیر نشان داده شده است.
نکته دیگر اینکه تکنولوژی Razor Pages از Attribute Routing پشتیبانی نمی کند. شما می توانید route template ها و اطلاعات مربوط به آنها را برای یک Razor Page به عنوان بخشی از دستور @pageتعریف کنید. این موضوع در کد زیر نشان داده شده است .
در مثال قبلی Page مورد نظر به یک Route به همراه یک پارامتر به نام ID که از نوع int خواهد بود نگاشت خواهد شد. برای مثال اگر یک Page به نام Products.cshtml در فولدر /Pages وجود داشته باشد این Page یک Route شبیه به قسمت زیر را خواهد داشت.
زمانی که یک Request با یک Route تطابق و یا نگاشت پیدا میکند و اما قبل از اینکه Action Method مورد نظر درMVC Asp.Net Core اجرا بشود عملیات model binding و model validation بر روی آن Request اعمال خواهد شد. وظیفه model binding تعبیر کردن داده های درون Request مورد نظر به تایپ های موجود در دات نت و البته تحویل دادن این طایفه ها به عنوان پارامترهای ورودیMethod Action مورد نظر می باشد. برای مثال اگر Method Action مورد نظر انتظار دریافت یک پارامتر به نام ID از نوع int را داشته باشند model binding سعی می کند که این پارامتر را از درون Request دریافت کرده و آن را به Method Action تحویل بدهد. برای انجام این کار مکانیزم model binding به دنبال Value هایی در فرمی که به سمت سرور ارسال شده است می گردد و در صورت نیافتن چنین دادهای نگاهی به خود Route و نهایتاً مقادیر query string خواهد کرد. با فرض اینکه مقدار پارامتر ID پیدا شود مکانیزم model binding آن را تبدیل به یک int کرده و سپس به عنوان پارامتر ورودی Action Method مورد نظر تحویل می دهد.
پس از انجام مکانیسم model binding و دقیقاً قبل از صدا زدن Action Method مورد نظر مکانیزم model validation اتفاق میافتد. model validation از Attribute های اختیاری که بر روی model type تعریف می کنید استفاده کرده و تلاش میکند که model object ها را بر اساس قوانینی که تعریف کرده اید اعتبارسنجی کند. با استفاده از model validation میتوانید مقادیر مورد نظرتان را به صورت الزامی و یا Required تعریف کنید. محدود کردن مقادیر به طول مشخص و یا بازه عددی مورد نظر نیز امکان پذیر است. اگر از validation attribute ها استفاده می کنید باید بدانید که مکانیزم model validation در صورت کشف کردن یک خطا در اعتبارسنجی Model پروپرتی IsValid مربوط به ModelState را با مقدار false تنظیم کرده و تعدادی از Validation rule های شکست خورده را به سمت کلاینت ارسال خواهد کرد.
اگر از مکانیسم model validation استفاده می کنید باید همواره اطمینان حاصل کنید که قبل از صدا زدن دستوراتی که state برنامه را تغییر میدهند معتبر بودن و یا valid بودن model را نیز بررسی کنید تا برنامه با استفاده از داده های نامعتبر دچار اختلال نشود. برای انجام این کار میتوان از فیلترها برای جلوگیری کردن از نیاز به کنترل هرAction Method مورد نظری استفاده کرد. در واقع درAsp.Net Core MVC ماهیت فیلترها روشی را در اختیار ما قرار می دهند تا با استفاده از آنها بتوانیم گروهی از Request ها را متوقف کرده و سیاست گذاری های مشترک و یا cross-cutting concern های خود را به صورت دسته ای تعریف کنیم در رابطه با cross-cutting concern ها در درس های بعدی صحبت خواهیم کرد.
فیلتر ها می توانند به Action های تک اعمال شده و یا حتی در سطح Controller و نهایتاً به صورت سراسری در سرتاسر برنامه اعمال بگردد. در توسعه Web API ها با استفاده از Asp.Net Core می توانید از قابلیت دیگری تحت عنوان content negotiation استفاده کنید. با استفاده از content negotiation درخواست های کاربران میتوانند قالب و یا فرمت پاسخ ها و یا Response های خود را مشخص کنند. این کار بر اساس Header هایی که در Request قرار میگیرند لحاظ میشوند. با استفاده از این کار Action هایی که داده هایی را به سمت کاربر ارسال می کنند می توانند داده های خود را در قالب هایی از قبیل XML و یا JSON قرار بدهند. این قابلیت کمک می کند تا یکی API یکسان بتواند توسط چندین کلاینت با چندین نیازمندی قالب داده ها مورد استفاده قرار بگیرد. در رابطه با این موضوع می توانید از بسته ی آموزش ویدئویی ساخت Web API در ASP.NET Core 2.0 دیدن کنید.
پروژه های Web API میبایست از یک Attribute به نام [ApiController] استفاده کنند و در هر کدام از Controller ها و یا یک Base Class برای Controller ها این Attribute را لحاظ نمایند. عملکرد این Attribute اضافه کردن Model validation خودکار خواهد بود و این بدان معناست که هر invalid model و یا مدل غیرمعتبری یک پاسخ ازنوع BadRequest را با جزئیات مربوط به validation errors ها برخواهد گرداند. علاوه بر این موضوع استفاده کردن از این Attribute باعث میشود تا تمامی Action ها نیاز به داشتن attribute route پیدا کنند و از conventional route استفاده ننمایند. نهایتا استفاده کردن از این Attribute باعث میشود تا جزئیات دقیقتری تحت ProblemDetails در Response هایی که در پاسخ به Error ها به سمت کلاینت ارسال می شود لحاظ بگردد.