تا اینجا یک پروژهی خالی ASP.NET Core 1.0 را به مرحلهی فعال سازی ASP.NET MVC و تنظیمات مسیریابیهای اولیهیآن رساندهایم. مرحلهی بعد، افزودن Viewها، نمایش اطلاعاتی به کاربران و دریافت اطلاعات از آنها است و همانطور که پیشتر نیز عنوان شد، برای «ارتقاء» نیاز است «15 مورد» ابتدایی مطالب ASP.NET MVC سایت را پیش از ادامهی این سری مطالعه کنید.
معرفی فایل جدید ViewImports
پروژهی خالی ASP.NET Core 1.0 فاقد پوشهی Views به همراه فایلهای آغازین آن است. بنابراین ابتدا در ریشهی پروژه، پوشهی جدید Views را ایجاد کنید.
فایلهای آغازین این پوشه هم در مقایسهی با نگارشهای قبلی ASP.NET MVC اندکی تغییر کردهاند. برای مثال در نگارش قبلی، فایل web.config ایی در ریشهی پوشهی Views قرار داشت و چندین مقصود را فراهم میکرد:
الف) در آن تنظیم شده بود که هر نوع درخواستی به فایلهای موجود در پوشهی Views، برگشت خورده و قابل پردازش نباشند. این مورد هم از لحاظ مسایل امنیتی اضافه شده بود و هم اینکه در ASP.NET MVC، برخلاف وب فرمها، شروع پردازش یک درخواست، از فایلهای View شروع نمیشود. به همین جهت است که درخواست مستقیم آنها بیمعنا است.
در ASP.NET Core، فایل web.config از این پوشه حذف شدهاست؛ چون دیگر نیازی به آن نیست. اگر مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایلهای استاتیک» را به خاطر داشته باشید، هر پوشهای که توسط میان افزار Static Files عمومی نشود، توسط کاربران برنامه قابل دسترسی نخواهد بود و چون پوشهی Views هم به صورت پیش فرض توسط این میان افزار عمومی نمیشود، نیازی به فایل web.config، جهت قطع دسترسی به فایلهای موجود در آن وجود ندارد.
ب) کاربرد دیگر این فایل web.config، تعریف فضاهای نام پیش فرضی بود که در فایلهای View مورد استفاده قرار میگرفتند. برای مثال چون فضای نام HTML Helperهای استاندارد ASP.NET MVC در این فایل web.config قید شده بود، دیگر نیازی به تکرار آن در تمام فایلهای View برنامه وجود نداشت. در ASP.NET Core، برای جایگزین کردن این قابلیت، فایل جدیدی را به نام ViewImports.cshtml_ معرفی کردهاند، تا دیگر نیازی به ارث بری از فایل web.config وجود نداشته باشد.
برای مثال اگر میخواهید بالای Viewهای خود، مدام ذکر using مربوط به فضای نام مدلها برنامه را انجام ندهید، این سطر تکراری را به فایل جدید view imports منتقل کنید:
و این فضاهای نام به صورت پیش فرض برای تمام viewها مهیا هستند و نیازی به تعریف مجدد، ندارند:
• System
• System.Linq
• System.Collections.Generic
• Microsoft.AspNetCore.Mvc
• Microsoft.AspNetCore.Mvc.Rendering
افزودن یک View جدید
در نگارشهای پیشین ASP.NET MVC، اگر بر روی نام یک اکشن متد کلیک راست میکردیم، در منوی ظاهر شده، گزینهی Add view وجود داشت. چنین گزینهای در نگارش RTM اول ASP.NET Core وجود ندارد و مراحل ایجاد یک View جدید را باید دستی طی کنید. برای مثال اگر نام کلاس کنترلر شما PersonController است، پوشهی Person را به عنوان زیر پوشهی Views ایجاد کرده و سپس بر روی این پوشه کلیک راست کنید، گزینهی add new item را انتخاب و سپس واژهی view را جستجو کنید:
البته یک دلیل این مساله میتواند امکان سفارشی سازی محل قرارگیری این پوشهها در ASP.NET Core نیز باشد که در ادامه آنرا بررسی خواهیم کرد (و ابزارهای از پیش تعریف شده عموما با مکانهای از پیش تعریف شده کار میکنند).
امکان پوشه بندی بهتر فایلهای یک پروژهی ASP.NET Core نسبت به مفهوم Areas در نگارشهای پیشین ASP.NET MVC
حالت پیش فرض پوشه بندی فایلهای اصلی برنامههای ASP.NET MVC، مبتنی بر فناوریها است؛ برای مثال پوشههای views و Controllers و امثال آن تعریف شدهاند.
روش دیگری را که برای پوشه بندی پروژههای ASP.NET MVC پیشنهاد میکنند (که Area توکار آن نیز زیر مجموعهی آن محسوب میشود)، اصطلاحا Feature Folder Structure نام دارد. در این حالت برنامه بر اساس ویژگیها و قابلیتهای مختلف آن پوشه بندی میشود؛ بجای اینکه یک پوشهی کلی کنترلرها را داشته باشیم و یک پوشهی کلی views را که پس از مدتی، ارتباط دادن بین اینها واقعا مشکل میشود.
هرکسی که مدتی با ASP.NET MVC کار کرده باشد حتما به این مشکل برخوردهاست. درحال پیاده سازی قابلیتی هستید و برای اینکار نیاز خواهید داشت مدام بین پوشههای مختلف برنامه سوئیچ کنید؛ از پوشهی کنترلرها به پوشهی ویووها، به پوشهی اسکریپتها، پوشهی اشتراکی ویووها و غیره. پس از رشد برنامه به جایی خواهید رسید که دیگر نمیتوانید تشخیص دهید این فایلی که اضافه شدهاست ارتباطش با سایر قسمتها چیست؟
ایدهی «پوشه بندی بر اساس ویژگیها»، بر مبنای قرار دادن تمام نیازهای یک ویژگی، درون یک پوشهی خاص آن است:
همانطور که مشاهده میکنید، در این حالت تمام اجزای یک ویژگی، داخل یک پوشه قرار گرفتهاند؛ از کنترلر مرتبط با Viewهای آن تا فایلهای css و js خاص آن.
برای پیاده سازی آن:
1) نام پوشهی Views را به Features تغییر دهید.
2) پوشهای را به نام StartupCustomizations به برنامه اضافه کرده و سپس کلاس ذیل را به آن اضافه کنید:
حالت پیش فرض ASP.NET MVC، یافتن فایلها در مسیرهای Views/{1}/{0}.cshtml و Views/Shared/{0}.cshtml است؛ که در اینجا {0} نام view است و {1} نام کنترلر. این ساختار هم در اینجا حفظ شدهاست؛ اما اینبار به پوشهی جدید Features اشاره میکند.
RazorViewEngine برنامه، بر اساس وهلهی پیش فرضی از اینترفیس IViewLocationExpander، محل یافتن Viewها را دریافت میکند. با استفاده از پیاه سازی فوق، این پیش فرضها را به پوشهی features هدایت کردهایم.
3) در ادامه به کلاس آغازین برنامه مراجعه کرده و پس از فعال سازی ASP.NET MVC، این قابلیت را فعال سازی میکنیم:
4) اکنون تمام فایلهای مرتبط با یک ویژگی را به پوشهی خاص آن انتقال دهید. منظور از این فایلها، کنترلر، فایلهای مدل و ویوومدل، فایلهای ویوو و فایلهای js و css هستند و نه مورد دیگری.
5) اکنون باید پوشهی Controllers خالی شده باشد. این پوشه را کلا حذف کنید. از این جهت که کنترلرها بر اساس پیش فرضهای ASP.NET MVC (کلاس ختم شدهی به کلمهی Controller واقع در اسمبلی که از وابستگیهای ASP.NET MVC استفاده میکند) در هر مکانی که تعریف شده باشند، یافت خواهند شد و پوشهی واقع شدن آنها مهم نیست.
6) در آخر به فایل project.json مراجعه کرده و قسمت publish آنرا جهت درج نام پوشهی Features اصلاح کنید (تا در حین توزیع نهایی استفاده شود):
در اینجا نیز یک نمونهی دیگر استفادهی از این روش بسیار معروف را مشاهده میکنید.
امکان ارائهی برنامه بدون ارائهی فایلهای View آن
ASP.NET Core به همراه یک EmbeddedFileProviderنیز هست. حالت پیش فرض آن PhysicalFileProvider است که بر اساس تنظیمات IViewLocationExpander توکار (و یا نمونهی سفارشی فوق در مبحث پوشهی ویژگیها) کار میکند.
برای راه اندازی آن ابتدا نیاز است بستهی نیوگت ذیل را به فایل project.json اضافه کرد:
سپس تنظیمات متد ConfigureServices کلاس آغازین برنامه را به صورت ذیل جهت معرفی EmbeddedFileProvider تغییر میدهیم:
و در آخر در فایل project.json، در قسمت build options، گزینهی embed را مقدار دهی میکنیم:
در اینجا چند نکته را باید مدنظر داشت:
1) اگر نام پوشهی Views را به Features تغییر دادهاید، نیاز به ثبت ViewLocationExpanders آنرا دارید (وگرنه، خیر).
2) در اینجا جهت مثال و بررسی اینکه واقعا این فایلها از اسمبلی برنامه خوانده میشوند، متد options.FileProviders.Clear فراخوانی شدهاست. این متد PhysicalFileProvider پیش فرض را حذف میکند. کار PhysicalFileProvider خواندن فایلهای ویووها از فایل سیستم به صورت متداول است.
3) کار قسمت embed در تنظیمات build، افزودن فایلهای cshtml به قسمت منابع اسمبلی است (به همین جهت دیگر نیازی به توزیع آنها نخواهد بود). اگر صرفا **/Features را ذکر کنید، تمام فایلهای موجود را پیوست میکند. همچنین اگر نام پوشهی Views را تغییر ندادهاید، این مقدار همان Views/**/*.cshtml خواهد بود و یا **/Views
4) در EmbeddedFileProvider میتوان هر نوع اسمبلی را ذکر کرد. یعنی میتوان برنامه را به صورت چندین و چند ماژول تهیه و سپس سرهم و یکپارچه کرد (options.FileProviders یک لیست قابل تکمیل است). در اینجا ذکر baseNamespace نیز مهم است. در غیر اینصورت منبع مورد نظر از اسمبلی یاد شده، قابل استخراج نخواهد بود (چون نام اسمبلی، قسمت اول نام هر منبعی است).
فعال سازی کامپایل خودکار فایلهای View در ASP.NET Core 1.0
این قابلیت به زودی جهت یافتن مشکلات موجود در فایلهای razor پیش از اجرای آنها، اضافه خواهد شد. اطلاعات بیشتر
معرفی فایل جدید ViewImports
پروژهی خالی ASP.NET Core 1.0 فاقد پوشهی Views به همراه فایلهای آغازین آن است. بنابراین ابتدا در ریشهی پروژه، پوشهی جدید Views را ایجاد کنید.
فایلهای آغازین این پوشه هم در مقایسهی با نگارشهای قبلی ASP.NET MVC اندکی تغییر کردهاند. برای مثال در نگارش قبلی، فایل web.config ایی در ریشهی پوشهی Views قرار داشت و چندین مقصود را فراهم میکرد:
الف) در آن تنظیم شده بود که هر نوع درخواستی به فایلهای موجود در پوشهی Views، برگشت خورده و قابل پردازش نباشند. این مورد هم از لحاظ مسایل امنیتی اضافه شده بود و هم اینکه در ASP.NET MVC، برخلاف وب فرمها، شروع پردازش یک درخواست، از فایلهای View شروع نمیشود. به همین جهت است که درخواست مستقیم آنها بیمعنا است.
در ASP.NET Core، فایل web.config از این پوشه حذف شدهاست؛ چون دیگر نیازی به آن نیست. اگر مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایلهای استاتیک» را به خاطر داشته باشید، هر پوشهای که توسط میان افزار Static Files عمومی نشود، توسط کاربران برنامه قابل دسترسی نخواهد بود و چون پوشهی Views هم به صورت پیش فرض توسط این میان افزار عمومی نمیشود، نیازی به فایل web.config، جهت قطع دسترسی به فایلهای موجود در آن وجود ندارد.
ب) کاربرد دیگر این فایل web.config، تعریف فضاهای نام پیش فرضی بود که در فایلهای View مورد استفاده قرار میگرفتند. برای مثال چون فضای نام HTML Helperهای استاندارد ASP.NET MVC در این فایل web.config قید شده بود، دیگر نیازی به تکرار آن در تمام فایلهای View برنامه وجود نداشت. در ASP.NET Core، برای جایگزین کردن این قابلیت، فایل جدیدی را به نام ViewImports.cshtml_ معرفی کردهاند، تا دیگر نیازی به ارث بری از فایل web.config وجود نداشته باشد.
برای مثال اگر میخواهید بالای Viewهای خود، مدام ذکر using مربوط به فضای نام مدلها برنامه را انجام ندهید، این سطر تکراری را به فایل جدید view imports منتقل کنید:
@using MyProject.Models
و این فضاهای نام به صورت پیش فرض برای تمام viewها مهیا هستند و نیازی به تعریف مجدد، ندارند:
• System
• System.Linq
• System.Collections.Generic
• Microsoft.AspNetCore.Mvc
• Microsoft.AspNetCore.Mvc.Rendering
افزودن یک View جدید
در نگارشهای پیشین ASP.NET MVC، اگر بر روی نام یک اکشن متد کلیک راست میکردیم، در منوی ظاهر شده، گزینهی Add view وجود داشت. چنین گزینهای در نگارش RTM اول ASP.NET Core وجود ندارد و مراحل ایجاد یک View جدید را باید دستی طی کنید. برای مثال اگر نام کلاس کنترلر شما PersonController است، پوشهی Person را به عنوان زیر پوشهی Views ایجاد کرده و سپس بر روی این پوشه کلیک راست کنید، گزینهی add new item را انتخاب و سپس واژهی view را جستجو کنید:
البته یک دلیل این مساله میتواند امکان سفارشی سازی محل قرارگیری این پوشهها در ASP.NET Core نیز باشد که در ادامه آنرا بررسی خواهیم کرد (و ابزارهای از پیش تعریف شده عموما با مکانهای از پیش تعریف شده کار میکنند).
امکان پوشه بندی بهتر فایلهای یک پروژهی ASP.NET Core نسبت به مفهوم Areas در نگارشهای پیشین ASP.NET MVC
حالت پیش فرض پوشه بندی فایلهای اصلی برنامههای ASP.NET MVC، مبتنی بر فناوریها است؛ برای مثال پوشههای views و Controllers و امثال آن تعریف شدهاند.
Project - Controllers - Models - Services - ViewModels - Views
هرکسی که مدتی با ASP.NET MVC کار کرده باشد حتما به این مشکل برخوردهاست. درحال پیاده سازی قابلیتی هستید و برای اینکار نیاز خواهید داشت مدام بین پوشههای مختلف برنامه سوئیچ کنید؛ از پوشهی کنترلرها به پوشهی ویووها، به پوشهی اسکریپتها، پوشهی اشتراکی ویووها و غیره. پس از رشد برنامه به جایی خواهید رسید که دیگر نمیتوانید تشخیص دهید این فایلی که اضافه شدهاست ارتباطش با سایر قسمتها چیست؟
ایدهی «پوشه بندی بر اساس ویژگیها»، بر مبنای قرار دادن تمام نیازهای یک ویژگی، درون یک پوشهی خاص آن است:
همانطور که مشاهده میکنید، در این حالت تمام اجزای یک ویژگی، داخل یک پوشه قرار گرفتهاند؛ از کنترلر مرتبط با Viewهای آن تا فایلهای css و js خاص آن.
برای پیاده سازی آن:
1) نام پوشهی Views را به Features تغییر دهید.
2) پوشهای را به نام StartupCustomizations به برنامه اضافه کرده و سپس کلاس ذیل را به آن اضافه کنید:
using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.Razor; namespace Core1RtmEmptyTest.StartupCustomizations { public class FeatureLocationExpander : IViewLocationExpander { public void PopulateValues(ViewLocationExpanderContext context) { context.Values["customviewlocation"] = nameof(FeatureLocationExpander); } public IEnumerable<string> ExpandViewLocations( ViewLocationExpanderContext context, IEnumerable<string> viewLocations) { return new[] { "/Features/{1}/{0}.cshtml", "/Features/Shared/{0}.cshtml" }; } } }
RazorViewEngine برنامه، بر اساس وهلهی پیش فرضی از اینترفیس IViewLocationExpander، محل یافتن Viewها را دریافت میکند. با استفاده از پیاه سازی فوق، این پیش فرضها را به پوشهی features هدایت کردهایم.
3) در ادامه به کلاس آغازین برنامه مراجعه کرده و پس از فعال سازی ASP.NET MVC، این قابلیت را فعال سازی میکنیم:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.Configure<RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new FeatureLocationExpander()); });
5) اکنون باید پوشهی Controllers خالی شده باشد. این پوشه را کلا حذف کنید. از این جهت که کنترلرها بر اساس پیش فرضهای ASP.NET MVC (کلاس ختم شدهی به کلمهی Controller واقع در اسمبلی که از وابستگیهای ASP.NET MVC استفاده میکند) در هر مکانی که تعریف شده باشند، یافت خواهند شد و پوشهی واقع شدن آنها مهم نیست.
6) در آخر به فایل project.json مراجعه کرده و قسمت publish آنرا جهت درج نام پوشهی Features اصلاح کنید (تا در حین توزیع نهایی استفاده شود):
"publishOptions": { "include": [ "wwwroot", "Features", "appsettings.json", "web.config" ] },
در اینجا نیز یک نمونهی دیگر استفادهی از این روش بسیار معروف را مشاهده میکنید.
امکان ارائهی برنامه بدون ارائهی فایلهای View آن
ASP.NET Core به همراه یک EmbeddedFileProviderنیز هست. حالت پیش فرض آن PhysicalFileProvider است که بر اساس تنظیمات IViewLocationExpander توکار (و یا نمونهی سفارشی فوق در مبحث پوشهی ویژگیها) کار میکند.
برای راه اندازی آن ابتدا نیاز است بستهی نیوگت ذیل را به فایل project.json اضافه کرد:
{ "dependencies": { //same as before "Microsoft.Extensions.FileProviders.Embedded": "1.0.0" },
services.AddMvc(); services.Configure<RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new FeatureLocationExpander()); var thisAssembly = typeof(Startup).GetTypeInfo().Assembly; options.FileProviders.Clear(); options.FileProviders.Add(new EmbeddedFileProvider(thisAssembly, baseNamespace: "Core1RtmEmptyTest")); });
"buildOptions": { "emitEntryPoint": true, "preserveCompilationContext": true, "embed": "Features/**/*.cshtml" },
1) اگر نام پوشهی Views را به Features تغییر دادهاید، نیاز به ثبت ViewLocationExpanders آنرا دارید (وگرنه، خیر).
2) در اینجا جهت مثال و بررسی اینکه واقعا این فایلها از اسمبلی برنامه خوانده میشوند، متد options.FileProviders.Clear فراخوانی شدهاست. این متد PhysicalFileProvider پیش فرض را حذف میکند. کار PhysicalFileProvider خواندن فایلهای ویووها از فایل سیستم به صورت متداول است.
3) کار قسمت embed در تنظیمات build، افزودن فایلهای cshtml به قسمت منابع اسمبلی است (به همین جهت دیگر نیازی به توزیع آنها نخواهد بود). اگر صرفا **/Features را ذکر کنید، تمام فایلهای موجود را پیوست میکند. همچنین اگر نام پوشهی Views را تغییر ندادهاید، این مقدار همان Views/**/*.cshtml خواهد بود و یا **/Views
4) در EmbeddedFileProvider میتوان هر نوع اسمبلی را ذکر کرد. یعنی میتوان برنامه را به صورت چندین و چند ماژول تهیه و سپس سرهم و یکپارچه کرد (options.FileProviders یک لیست قابل تکمیل است). در اینجا ذکر baseNamespace نیز مهم است. در غیر اینصورت منبع مورد نظر از اسمبلی یاد شده، قابل استخراج نخواهد بود (چون نام اسمبلی، قسمت اول نام هر منبعی است).
فعال سازی کامپایل خودکار فایلهای View در ASP.NET Core 1.0
این قابلیت به زودی جهت یافتن مشکلات موجود در فایلهای razor پیش از اجرای آنها، اضافه خواهد شد. اطلاعات بیشتر