Page یا «صفحه» در Razor، یکی از ویژگیهای جدید در ASP.NET Core MVC است که تمرکز کدنویسی را بر روی صفحات قرار میدهد و این موجب راحتی کدنویسی و بالارفتن راندمان میشود. این «صفحات» نیازمند استفاده از نسخۀ ASP.NET Core 2.0.0 و نسخههای بعد از آن هستند که در Visual Studio 2017 Update 3 و نسخههای بعدی در دسترس است.
«صفحات» Razor بهطور پیشفرض در MVC در دسترس است و کافیست در فایل Startup.cs، «صفحات» Razor فعال شود:
public class Startup
{
public void ConfigureServices(IServiceCollections services)
{
services.AddMvc(); // موجب فعال شدن «صفحات» و کنترلرها میشود
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
}
}
تمام ویژگیهای جدید «صفحات» Razor در اسمبلی
Microsoft.AspNetCore.Mvc.RazorPages در دسترس است و کافیست بدان ارجاع دهید. همچنین اگر ارجاعی به پکیج Microsoft.AspNetCore.Mvc داشته باشید نیز «صفحات» Razor در دسترس خواهند بود. «صفحۀ» زیر را در نظر بگیرید:
@page
@{
var message = "Hello, World!";
}
<html>
<body>
<p>@message</p>
</body>
</html>
این کد، بسیار شبیه «صفحات» ویوی Razor معمولی است؛ اما آنچه آن را متفاوت میسازد، استفاده از دایرکتیو page@ است. با استفاده از page@، درواقع این فایل نقش اکشن متد MVC را نیز انجام میدهد و بدین معناست که میتواند بهطورمستقیم، درخواستها را بدون وساطت هیچ کنترلری مدیریت کند. دایرکتیو page@، باید در ابتدای صفحۀ Razor قرار بگیرد. این دایرکتیو رفتار اصلی سایر Razorها را تحت تأثیر قرار میدهد.
ارتباط میان مسیرهای URL با صفحات، از طریق محل قرارگیری «صفحات» در فایل سیستم، اتفاق میافتد. جدول زیر نحوۀ ارتباط میان مسیر «صفحات» Razor و URL متناظر آن را نشان میدهد:
URL متناظر | نام فایل و مسیر آن |
/ یا /Index | /Pages/Index.cshtml |
/Contact | /Pages/Store/Contact.cshtml |
/Store/Contact | /Pages/Store/Contact.cshtml |
برنامه هنگام اجرا، بهطور پیشفرض، برای یافتن فایل «صفحات» Razor در پوشۀ «صفحات» جستجو میکند.
ویژگیهای جدید «صفحات» Razor بدین منظور طراحی شدهاند تا الگوهای عمومی بهکار رفته در پیمایشگر صفحات وب را آسانتر کنند. صفحۀ «تماس با ما» را در نظر بگیرید که در آن مدل Contact پیادهسازی شده است. فایل MyApp/Contact.cs بدین شکل است:
using System.ComponentModel.DataAnnotations;
namespace MyApp
{
public class Contact
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
}
}
و فایل ویوی آن یعنی
MyApp/Pages/Contact.cshtml به شکل زیر است: @page
@using MyApp
@using Microsoft.AspNetCore.Mvc.RazorPages
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@inject ApplicationDbContext Db
@functions {
[BindProperty]
public Contact Contact { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
Db.Contacts.Add(Contact);
await Db.SaveChangesAsync();
return RedirectToPage();
}
return Page();
}
}
<html>
<body>
<p>فرم زیر را پر کنید تا در اسرع وقت، کارشناسان ما با شما بگیرند</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div>Name: <input asp-for="Contact.Name" /></div>
<div>Email: <input asp-for="Contact.Email" /></div>
<input type="submit" />
</form>
</body>
</html>
این «صفحه»، هندلری با عنوان OnPostAsync دارد که هنگام ارسال درخواستهای POST (هنگامیکه کاربری فرم را ثبت میکند) اجرا میشود. البته میتوان برای هر نوع تقاضای HTTP، هندلری را اضافه کرد که معمولاً از OnGet برای هرگونه تقاضای نشان دادن یک صفحۀ HTML استفاده میشود و از OnPost برای ارسال اطلاعات از طریق فرم استفاده میشود و همانطور که میدانیم پسوند Async اختیاری است. اما اغلب بهطور قراردادی بهکار برده میشود. کد نوشته شده داخل OnPostAsync بسیار شبیه چیزی است که بهطور معمول در داخل یک کنترلر نوشته میشود. این الگویی است برای صفحاتی که در آنها بیشتر اصول اوّلیۀ MVC از قبیل بایند کردن مدلها، اعتبارسنجی و نتایج اکشنها قابل استفاده است.
روند اصلی OnPostAsync بهشکل زیر است:
کنترل خطاهای اعتبارسنجی.
- اگر خطایی نبود، اطلاعات ذخیره شده و به صفحۀ دیگر ریدایرکت میشود.
- درغیراینصورت، صفحه را دوباره بههمراه خطاهای اعتبار سنجی نمایش میدهد.
- اگر اطلاعات موفقتآمیز وارد شوند، آنگاه متد هندلر OnPostAsync، هلپر RedirctToPage را برای برگرداندن نمونهای از RedirectToPageResult فراخوانی میکند. این یک نوع جدید بازگشتی برای اکشن متد است که شبیه RedirectToAction یا RedirectToRoute است؛ با این تفاوت که مخصوص صفحات طراحی شده است.
هنگامیکه فرم ثبت شده، خطای اعتبارسنجی داشته باشد، آنگاه متد هندلر OnPostAsync هلپر متد Page را فراخوانی میکند. این هلپر یک نمونه از PageResult را برمیگرداند. این شبیه کاری است که اکشنها در کنترلرها، یک ویو را برگردانند. PageResult خروجی پیشفرض یک هندلر متد است و هندلر متدی که نوع void را برگرداند «صفحۀ» جاری را رندر میکند.
خصیصۀ Contact از attribute جدید [BindProperty] برای مقید کردن مدل استفاده میکند. «صفحات» بهطور پیشفرض، ویژگیهایی را که GET نیستند، مقید میکنند. مقیدکردن به خواص، موجب کاهش کدی میشود که شما باید در فرم مورد نظر خود بیاورید؛ مثلاً بهراحتی میتوان نوشت ( <input asp-for="Contacts.Name" /> ) که با این کار مقیدسازی فیلد و خصیصۀ مورد نظر بهطور خودکار انجام میشود.
بهجای استفاده از دایرکتیو model@ در اینجا، از ویژگی جدید «صفحات» استفاده میکنیم. بهطور پیشفرض دایرکتیو کلاس Page همان مدل است و ویژگیهایی شبیه مقیدکردن مدلها، تگ هلپرها و هلپرهای HTML، همگی فقط با ویژگیهایی که درون functions@ نوشته شدهاند، کار میکنند. هنگام استفاده از «صفحات» بهطور خودکار به ویومدل دسترسی وجود دارد.
دایرکتیو inject@ قبل از شروع هندلر متد، قرار گرفته و برای تزریق وابستگی در «صفحات» استفاده میشود که همانند Razor ویوهای قبل کار میکند. متغیر Db در اینجا از نوع ApplicationDbContext است که به «صفحه» تزریق میشود.
در اینجا نیازی به نوشتن هیچ دستوری برای اعتبارسنجی anti-forgery نیست. تولید و اعتبارسنجی anti-forgery بهطور خودکار در «صفحات» انجام میشود و برای دستیابی به این ویژگی امنیتی نیاز به تنظیمات اضافهتری نیست.
میتوان ویوی MyApp/Pages/Contact.chsml را از مدل آن به شکل زیر جدا کرد:
@page
@using MyApp
@using MyApp.Pages
@using Microsoft.AspNetCore.Mvc.RazorPages
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@model ContactModel
<html>
<body>
<p>فرم زیر را پر کنید تا در اسرع وقت، کارشناسان ما با شما بگیرند </p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div>Name: <input asp-for="Contact.Name" /></div>
<div>Email: <input asp-for="Contact.Email" /></div>
<input type="submit" />
</form>
</body>
</html>
و بخش مدل «صفحه» را به شکل زیر:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MyApp.Pages
{
public class ContactModel : PageModel
{
public ContactModel(ApplicationDbContext db)
{
Db = db;
}
[BindProperty]
public Contact Contact { get; set; }
private ApplicationDbContext Db { get; }
public async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
Db.Contacts.Add(Contact);
await Db.SaveChangesAsync();
return RedirectToPage();
}
return Page();
}
}
}
براساس قرارداد، کلاس باید بهشکل
PageModel باشد و در همان فضای نامی باشد که صفحۀ آن قرار دارد و نیازی به استفاده از
functions @ نیست و تنها تغییر آن، تزریق وابستگی از طریق کلاس سازنده است.
با استفاده از PageModel میتوان از الگوی آزمون واحد بهره برد؛ اما مستلزم نوشتن صریح کلاس و سازندۀ آن است. مزیت «صفحات» بدون فایل PageModel این است که از کامپایل در زمان اجرا پشتیبانی میکنند که این قابلیت در هنگام کدنویسی مفید است.
ادامه دارد...