عنوان:

‫بررسی تغییرات Blazor 8x - قسمت ششم - نکات تکمیلی ویژگی راهبری بهبود یافته‌ی صفحات SSR


نویسنده: وحید نصیری
تاریخ: ۱۴۰۲/۰۸/۲۴ ۱۲:۳۰
آدرس: www.dntips.ir


در قسمت قبل، در حین بررسی رفتار جزیره‌های تعاملی Blazor Server، نکته‌ی زیر را هم درباره‌ی راهبری صفحات SSR مرور کردیم:
« اگر دقت کنید، جابجایی بین صفحات، با استفاده از fetch انجام شده؛ یعنی با اینکه این صفحات در اصل static HTML خالص هستند، اما ... کار full reload صفحه مانند ASP.NET Web forms قدیمی انجام نمی‌شود (و یا حتی برنامه‌های MVC و Razor pages) و نمایش صفحات، Ajax ای است و با fetch استاندارد آن صورت می‌گیرد تا هنوز هم حس و حال SPA بودن برنامه حفظ شود. همچنین اطلاعات DOM کل صفحه را هم به‌روز رسانی نمی‌کند؛ فقط موارد تغییر یافته در اینجا به روز رسانی خواهند شد.»
در این قسمت، نکات تکمیلی این قابلیت را که به آن enhanced navigation هم گفته می‌شود، بررسی می‌کنیم.


روش غیرفعال کردن راهبری بهبودیافته برای بعضی از لینک‌ها

ویژگی راهبری بهبودیافته فقط در حین هدایت بین صفحات مختلف یک برنامه‌ی Blazor 8x SSR، فعال است. اگر در این بین، کاربری به یک صفحه‌ی غیر بلیزری هدایت شود، راهبری بهبود یافته شکست خورده و سعی می‌کند حالت full document load را پیاده سازی و اجرا کند. مشکل اینجاست که در این حالت دو درخواست ارسال می‌شود: ابتدا حالت راهبری بهبودیافته فعال می‌شود و در ادامه پس از شکست این راهبری، هدایت مستقیم صورت می‌گیرد. برای رفع این مشکل می‌توان ویژگی جدید data-enhance-nav را با مقدار false، به لینک‌های خارجی مدنظر اضافه کرد تا برای این حالت‌ها دیگر ویژگی راهبری بهبودیافته فعال نشود:
<a href="/not-blazor" data-enhance-nav="false">A non-Blazor page</a>


فعالسازی مدیریت بهبودیافته‌ی فرم‌های SSR

در قسمت چهارم این سری با فرم‌های جدید SSR مخصوص Blazor 8x آشنا شدیم. این فرم‌ها هم می‌توانند از امکانات راهبری بهبود یافته استفاده کنند (یعنی مدیریت ارسال آن، توسط fetch API انجام شده و به روز رسانی قسمت‌های تغییریافته‌ی صفحه را Ajax ای انجام دهند)؛ برای نمونه اینبار همانند تصویر زیر، از fetch استاندارد برای ارسال اطلاعات به سمت سرور کمک گرفته می‌شود (یعنی عملیات Ajax ای شده‌؛ بجای یک post-back معمولی):


 اما ... این قابلیت به صورت پیش‌فرض در فرم‌های تعاملی SSR غیرفعال است. چون همانطور که عنوان شد، اگر مقصد این فرم، یک آدرس غیربلیزری باشد، دوبار ارسال فرم صورت خواهد گرفت؛ یکبار با استفاده از fetch API و بار دیگر پس از شکست، به صورت معمولی. اما اگر مطمئن هستید که endpoint این فرم، قطعا یک کامپوننت بلیزری است، بهتر است این قابلیت را در یک چنین فرم‌هایی نیز به صورت زیر فعال کنید:
<form method="post" @onsubmit="() => submitted = true" @formname="name" data-enhance>
    <AntiforgeryToken />
    <InputText @bind-Value="Name" />
    <button>Submit</button>
</form>

@if (submitted)
{
    <p>Hello @Name!</p>
}

@code {

    bool submitted;

    [SupplyParameterFromForm]
    public string Name { get; set; } = "";
}
البته باتوجه به اینکه در اینجا هم می‌توان از EditForm‌ها استفاده کرد، در این حالت فقط کافی است ویژگی Enhance را به آن‌ها اضافه نمائید:
<EditForm method="post" Model="NewCustomer" OnValidSubmit="() => submitted = true" FormName="customer" Enhance>
    <DataAnnotationsValidator />
    <ValidationSummary/>
    <p>
        <label>
            Name: <InputText @bind-Value="NewCustomer.Name" />
        </label>
    </p>
    <button>Submit</button>
</EditForm>

@if (submitted)
{
    <p id="pass">Hello @NewCustomer.Name!</p>
}

@code {
    bool submitted = false;

    [SupplyParameterFromForm]
    public Customer? NewCustomer { get; set; }

    protected override void OnInitialized()
    {
        NewCustomer ??= new();
    }

     public class Customer
    {
        [StringLength(3, ErrorMessage = "Name is too long")]
        public string? Name { get; set; }
    }
}

نکته‌ی مهم: در این حالت فرض بر این است که هیچگونه هدایتی به یک Non-Blazor endpoint صورت نمی‌گیرید؛ وگرنه با یک خطا مواجه خواهید شد.



غیرفعال کردن راهبری بهبودیافته برای قسمتی از صفحه

اگر با استفاده از جاواسکریپت و خارج از کدهای بیلزر، اطلاعات DOM را به‌روز رسانی می‌کنید، ویژگی راهبری بهبودیافته، از آن آگاهی نداشته و به صورت خودکار تمام تغییرات شما را بازنویسی می‌کند. به همین جهت اگر نیاز است قسمتی از صفحه را که مستقیما توسط کدهای جاواسکریپتی تغییر می‌دهید، از به‌روز رسانی‌های این قابلیت مصون نگه‌دارید، می‌توانید ویژگی جدید data-permanent را به آن قسمت اضافه کنید:
<div data-permanent>
    Leave me alone! I've been modified dynamically.
</div>


امکان آگاه شدن از بروز راهبری بهبودیافته در کدهای جاواسکریپتی

اگر به هردلیلی در کدهای جاواسکریپتی خودنیاز به آگاه شدن از وقوع یک هدایت بهبودیافته را دارید (برای مثال جهت بازنویسی تغییرات ایجاد شده‌ی توسط آن)، می‌توانید به نحو زیر، مشترک رخ‌دادهای آن شوید:
<script>
    Blazor.addEventListener('enhancedload', () => {
        console.log('enhanced load event occurred');
    });
</script>


ویژگی جدید Named Element Routing در Blazor 8x

Blazor 8x از ویژگی مسیریابی سمت کلاینت به کمک تعریف URL fragments پشتیبانی می‌کند. به این صورت رسیدن (اسکرول) به یک قسمت از صفحه‌ای طولانی، بسیار ساده می‌شود.
برای مثال المان h2 با id مساوی targetElement را درنظر بگیرید:
<div class="border border-info rounded bg-info" style="height:500px"></div>

<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>
در Blazor 8x برای رسیدن به آن، هر سه حالت زیر میسر هستند:
<a href="/counter#targetElement">

<NavLink href="/counter#targetElement">

Navigation.NavigateTo("/counter#targetElement");


معرفی متد جدید Refresh در Blazor 8x

در Blazor 8x، امکان بارگذاری مجدد صفحه با فراخوانی متد جدید NavigationManager.Refresh(bool forceLoad = false) میسر شده‌است. این متد در حالت پیش‌فرض از قابلیت راهبری بهبودیافته برای به روز رسانی صفحه استفاده می‌کند؛ مگر اینکه اینکار میسر نباشد. اگر آن‌را با پارامتر true فراخوانی کنید، full-page reload رخ خواهد داد.
همین اتفاق در مورد متد Navigation.NavigateTo نیز رخ‌داده‌است. این متد نیز در Blazor 8x به صورت پیش‌فرض بر اساس قابلیت راهبری بهبود یافته کار می‌کند؛ مگر اینکه اینکار میسر نباشد و یا پارامتر forceLoad آن‌را به true مقدار دهی کنید.

نظرات

  • وحید نصیری در ۱۴۰۳/۰۸/۰۲ ۰۸:۲۰
    یک نکته‌ی تکمیلی: هدایت کاربر به بالای صفحه با تغییر آدرس جاری

    زمانیکه از enhanced navigation استفاده می‌کنیم، اگر آدرسی در میانه‌ی صفحه درخواست شود، موقعیت scroll جاری حفظ می‌شود. این حفظ موقعیت برای درخواست‌های post مفید است و برنامه‌ را شبیه به برنامه‌های SPA می‌کند؛ اما برای حالت Get که واقعا هدف، هدایت به یک صفحه‌ی جدید است، خوشایند نیست و می‌خواهیم که کاربر، به بالای صفحه هدایت شود و صفحه‌ی جدید را از ابتدای و بالای آن مشاهده کند و نه از نیمه‌ی آن! برای رفع این مشکل می‌توان به روش‌های زیر عمل کرد:
    الف) enhanced navigation را به‌طور کامل خاموش کنیم که روش انجام آن به ازای یک لینک به صورت زیر است:
    <a href="redirect" data-enhance-nav="false">
        GET without enhanced navigation
    </a>
    و برای کل برنامه به این صورت:
    <script src="/_framework/blazor.web.js" autostart="false"></script>
    <script>
        Blazor.start({
            ssr: { disableDomPreservation: true }
        });
    </script>

    ب) فقط برای حالت‌های Get و تعویض آدرس صفحه، اگر این تغییر آدرس از طریق enhanced navigation بود (با گوش فرا دادن به رخ‌داد enhancedload)، کاربر را با فراخوانی متد window.scrollTo، به صورت خودکار به بالای صفحه هدایت کنیم:
    <script>
        window.interceptNavigation = () => {
            let currentUrl = window.location.href;
            Blazor.addEventListener('enhancedload', () => {
                let newUrl = window.location.href;
                if (currentUrl != newUrl) {
                    window.scrollTo({ top: 0, left: 0, behavior: 'instant' });
                }
                currentUrl = newUrl;
            });
        };
    
        document.onload += window.interceptNavigation();
    </script>
  • وحید نصیری در ۱۴۰۵/۰۳/۲۸ ۰۹:۵۵
    اعتبار‌سنجی سمت کاربر در Blazor 11x SSR؛ تجربه کاربری بهتر بدون نیاز به Interactive WebAssembly یا Server

    یکی از چالش‌های همیشگی در توسعه فرم‌های وب با استفاده از معماری رندر سمت سرور (SSR)، تاخیر زمانی ناشی از ارسال اطلاعات به سرور برای جزیی‌ترین اعتبار‌سنجی‌ها (Validation) بوده است. این موضوع علاوه بر کاهش کیفیت تجربه کاربری (UX)، بار پردازشی اضافه‌ای را نیز به سرور تحمیل می‌کند. در فریم‌ورک Blazor، برای داشتن اعتبار‌سنجی آنی و بدون تاخیر (Instant Feedback)، توسعه‌دهندگان معمولاً ناچار بودند به سراغ حالت‌های تعاملی یا Interactive (مانند Blazor Server یا Blazor WebAssembly) بروند.
    با این حال، در آپدیت‌های جدید دات‌نت (از طریق قابلیت‌های معرفی شده در مخزن ASP.NET Core)، Blazor SSR اکنون به طور بومی از اعتبار‌سنجی سمت کاربر (Client-Side Validation) پشتیبانی می‌کند. این قابلیت به توسعه‌دهندگان اجازه می‌دهد بدون نیاز به راه‌اندازی مدارهای سنکرون SignalR یا دانلود سنگین فایل‌های WebAssembly، تجربه‌ای سریع، روان و مشابه با اپلیکیشن‌های MVC (مجهز به Unobtrusive Validation) را به کاربران ارائه دهند.

    توضیح و بررسی عمیق مکانیزم عملکرد
    بزرگ‌ترین مزیت این قابلیت جدید این است که مدل دات‌نت (.NET Model) همچنان به عنوان تنها مرجع حقیقت (Single Source of Truth) برای قوانین اعتبار‌سنجی باقی می‌ماند. شما نیازی به نوشتن کدهای موازی جاوااسکریپت برای چک کردن فرم‌ها ندارید.

    نحوه کارکرد سیستم به چه صورت است؟
    • رندر متادیتا توسط سرور: هنگام رندر شدن فرم در سمت سرور، ویژگی‌های اعتبار‌سنجی دات‌نت (Data Annotations) به متادیتاهای خاصی تبدیل شده و در ساختار HTML فرم تزریق می‌شوند.
    • اجرا توسط جاوااسکریپت بلئیزور: کدهای جاوااسکریپت داخلیِ Blazor (بسته به نوع فرم Enhanced یا معمولی)، این متادیتاها را در مرورگر کاربر رهگیری کرده و پیش از ارسال (Submit) فرم به سرور، قوانین را به صورت آنی اعمال می‌کنند.
    نکته تکمیلی: این ویژگی به صورت پیش‌فرض برای تمامی فرم‌های SSR که از کامپوننت استفاده می‌کنند، فعال است. نکته جالب اینجاست که این قابلیت هم از فرم‌های معمولی و هم از فرم‌های بهبودیافته (Enhanced Forms که با استفاده از Fetch API فرم را ارسال می‌کنند و صفحه را کاملاً Refresh نمی‌کنند) پشتیبانی می‌کند.

    پیاده‌سازی عملی (نمونه کد استاندارد)
    برای استفاده از این قابلیت، کافی است از کامپوننت‌های استاندارد Blazor مانند EditForm و DataAnnotationsValidator استفاده کنید. در ادامه یک نمونه فرم ثبت‌نام ساده و خوانا آورده شده است:
    ۱. بخش کدهای فرانت‌اند (Razor Component)
    <EditForm Model="Model" Enhance FormName="registration" OnValidSubmit="HandleValidSubmit">
        <DataAnnotationsValidator />
    
        <div>
            <label for="Email">ایمیل</label>
            <InputText @bind-Value="Model!.Email" id="Email" />
            <ValidationMessage For="@(() => Model!.Email)" />
        </div>
    
        <div>
            <label for="Password">رمز عبور</label>
            <InputText @bind-Value="Model!.Password" id="Password" type="password" />
            <ValidationMessage For="@(() => Model!.Password)" />
        </div>
    
        <div>
            <label for="ConfirmPassword">تکرار رمز عبور</label>
            <InputText @bind-Value="Model!.ConfirmPassword" id="ConfirmPassword" type="password" />
            <ValidationMessage For="@(() => Model!.ConfirmPassword)" />
        </div>
    
        <div>
            <button type="submit" id="submit-btn">ثبت نام</button>
        </div>
    </EditForm>
    
    @code {
        [SupplyParameterFromForm]
        private RegistrationModel? Model { get; set; }
    
        protected override void OnInitialized() => Model ??= new();
    
        private void HandleValidSubmit() 
        {
            // این متد تنها زمانی اجرا می‌شود که اعتبار‌سنجی سمت کاربر و سمت سرور هر دو پاس شوند.
        }
    }
    ۲. بخش مدل داده (Data Model)
    using System.ComponentModel.DataAnnotations;
    
    public class RegistrationModel
    {
        [Required(ErrorMessage = "وارد کردن ایمیل الزامی است.")]
        [EmailAddress(ErrorMessage = "فرمت ایمیل وارد شده صحیح نیست.")]
        public string Email { get; set; }
    
        [Required(ErrorMessage = "وارد کردن رمز عبور الزامی است.")]
        [StringLength(100, MinimumLength = 8, ErrorMessage = "رمز عبور باید حداقل ۸ کاراکتر باشد.")]
        public string Password { get; set; }
    
        [Required(ErrorMessage = "تکرار رمز عبور الزامی است.")]
        [Compare("Password", ErrorMessage = "رمز عبور و تکرار آن مطابقت ندارند.")]
        [Display(Name = "Confirm Password")]
        public string ConfirmPassword { get; set; }
    }

    نکات کلیدی جهت امنیت (Best Practices)
    اگرچه اعتبار‌سنجی سمت کاربر تجربه فوق‌العاده‌ای به کاربر می‌دهد، اما به عنوان یک توسعه‌دهنده ارشد همیشه باید به یاد داشته باشید:
    • امنیت هرگز فرار نمی‌کند: کدهای سمت کاربر (مرورگر) همیشه قابل دستکاری هستند. Blazor به صورت خودکار پس از ارسال فرم، اعتبار‌سنجی سمت سرور را نیز مجدداً انجام می‌دهد تا از امنیت و صحت داده‌ها مطمئن شود.
    • پشتیبانی از لود سریع‌تر: با این روش، حجم پکیج اولیه ارسالی به مرورگر بسیار ناچیز باقی می‌ماند، چرا که نیازی به دانلود Runtime حجیم WebAssembly (.NET WASM) برای یک اعتبار‌سنجی ساده نیست.

    نتیجه‌گیری
    اضافه شدن قابلیت اعتبار‌سنجی سمت کاربر به Blazor SSR (طبق مستندات و تسک‌های دات‌نت نظیر تغییرات #66441)، شکاف بزرگ بین برنامه‌های سنتی SSR و برنامه‌های مدرن SPA را پر کرده است. شما اکنون می‌توانید سرعت و بازخورد آنی فرم‌های تعاملی را در کنار سبکی، سئو‌پذیری (SEO-friendliness) و سرعت لود بی‌نظیر Static SSR در اختیار داشته باشید. این ویژگی، Blazor را به یکی از قدرتمندترین ابزارها برای توسعه بک‌اند و فرانت‌اند یکپارچه در اکوسیستم دات‌نت تبدیل می‌کند.