1) بهروز رسانی شماره نگارش داتنت
اولین قدم در جهت ارتقاء پروژههای قدیمی، تغییر شماره نگارش TargetFramework موجود در فایل csproj. به net8.0 است. پس از اینکار نیاز است تمام بستههای نیوگت موجود را نیز به نگارشهای جدیدتر آنها ارتقاء دهید.
2) فعالسازی حالت SSR تعاملی سمت سرور
پایهی تمام تغییرات انجام شدهی در Blazor 8x، قابلیت SSR است و تمام امکانات دیگر برفراز آن اجرا میشوند. به همین جهت پس از ارتقاء شماره نگارش داتنت، نیاز است SSR را فعال کنیم و برای اینکار باید به هاست ASP.NET Core بگوئیم که درخواستهای رسیده را به کامپوننتهای Razor هدایت کند. بنابراین، به فایل Program.cs مراجعه کرده و دو تغییر زیر را به آن اعمال کنید:
// ... builder.Services.AddRazorComponents().AddInteractiveServerComponents(); // ... app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
در اینجا ترکیب کامپوننتهای تعاملی سمت سرور (AddInteractiveServerComponents) و رندر تعاملی سمت سرور (AddInteractiveServerRenderMode)، دقیقا همان Blazor Server قدیمی است که ما با آن آشنا هستیم.
یک نکته: اگر از قالب جدید dotnet new blazor --interactivity None استفاده کنیم، یعنی حالت تعاملی بودن آنرا به None تنظیم کنیم، کلیات ساختار پروژهای را که مشاهده خواهیم کرد، با حالت تعاملی Server آن یکی است؛ فقط در تنظیمات Program.cs آن، گزینههای فوق را نداریم و به صورت زیر ساده شدهاست:
// ... builder.Services.AddRazorComponents(); // ... app.MapRazorComponents<App>();
3) ایجاد فایل جدید App.razor
در دات نت 8، دیگر خبری از فایل آغازین Host.cshtml_ پروژههای Blazor Server قدیمی نیست و کدهای آن با تغییراتی، به فایل جدید App.razor منتقل شدهاند. در این قسمت، کار هدایت درخواستهای رسیده به کامپوننتهای برنامه رخ میدهد و از این پس، صفحهی ریشهی برنامه خواهد بود.
در این تصویر، مقایسهای را بین جریان پردازش یک درخواست رسیده در دات نت 8، با نگارش قبلی Blazor Server مشاهده میکنید. در دات نت 8، فایل Host.cshtml_ (یک Razor Page آغازین برنامه) با یک کامپوننت Razor به نام App.razor جایگزین شدهاست و فایل قدیمی App.razor این پروژهها به Routes.razor، تغییر نام یافتهاست.
نمونهای از فایل App.razor جدید را که در قسمت قبل نیز معرفی کردیم، در اینجا با جزئیات بیشتری بررسی میکنیم:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <base href="/" /> <link rel="stylesheet" href="bootstrap/bootstrap.min.css" /> <link rel="stylesheet" href="app.css" /> <link rel="stylesheet" href="MyApp.styles.css" /> <link rel="icon" type="image/png" href="favicon.png" /> <HeadOutlet /> </head> <body> <Routes /> <script src="_framework/blazor.web.js"></script> </body> </html>
- تمام دایرکتیوهای تعریف شده مانند page ،@addTagHelper@ و غیره حذف شدهاند.
- base href تعریف شده اینبار فقط با یک / شروع میشود و نه با /~. این مورد خیلی مهم است! اگر به آن دقت نکنید، هیچکدام از فایلهای استاتیک برنامه مانند فایلهای css. و js.، بارگذاری نخواهند شد!
- پیشتر برای رندر HeadOutlet، از یک تگهلپر استفاده میشد. این مورد در نگارش جدید با یک کامپوننت ساده جایگزین شدهاست.
- تمام component tag helperهای پیشین حذف شدهاند و نیازی به آنها نیست.
- ارجاع پیشین فایل blazor.server.js با فایل جدید blazor.web.js جایگزین شدهاست.
یک نکته: همانطور که مشاهده میکنید، فایل App.razor یک کامپوننت است و اینبار به همراه تگ <script> نیز شدهاست. یعنی در این نگارش از Blazor میتوان اسکریپتها را در کامپوننتها نیز ذکر کرد؛ فقط با یک شرط! این کامپوننت حتما باید SSR باشد. اگر این تگ اسکریپتی را در یک کامپوننت تعاملی ذکر کنید، همانند قابل (و نگارشهای پیشین Blazor) با خطا مواجه خواهید شد.
4) ایجاد فایل جدید Routes.razor و مدیریت سراسری خطاها و صفحات یافت نشده
همانطور که عنوان شد، فایل قدیمی App.razor این پروژهها به Routes.razor تغییر نام یافتهاست که درج آنرا در قسمت body مشاهده میکنید. محتوای این فایل نیز به صورت زیر است:
<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> </Router>
app.UseStatusCodePagesWithRedirects("/StatusCode/{0}");
@page "/StatusCode/{responseCode}" <h3>StatusCode @ResponseCode</h3> @code { [Parameter] public string? ResponseCode { get; set; } }
یک نکته: اگر پروژهای را بر اساس قالب dotnet new blazor --interactivity Server ایجاد کنیم، در فایل Program.cs آن، چنین تنظیمی اضافه شدهاست:
if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); }
سؤال: در اینجا (برنامههای Blazor Server) چه تفاوتی بین UseExceptionHandler و UseStatusCodePagesWithRedirects وجود دارد؟
میانافزار UseExceptionHandler برای مدیریت استثناءهای آغازین برنامه، پیش از تشکیل اتصال دائم SignalR وارد عمل میشود. پس از آن و تشکیل اتصال وبسوکت مورد نیاز، فقط از میانافزار UseStatusCodePagesWithRedirects استفاده میکند.
اگر علاقمند نیستید تا تمام خطاهای رسیده را همانند مثال فوق در یک صفحه مدیریت کنید، میتوانید حداقل سه فایل زیر را به برنامه اضافه کنید تا خطاهای متداول یافت نشدن آدرسی، بروز خطایی و یا عدم دسترسی را مدیریت کنند:
404.razor
@page "/StatusCode/404" <PageTitle>Not found</PageTitle> <h1>Not found</h1> <p role="alert">Sorry, there's nothing at this address.</p>
500.razor
@page "/StatusCode/500" <PageTitle>Unexpected error</PageTitle> <h1>Unexpected error</h1> <p role="alert">There was an unexpected error.</p>
401.razor
@page "/StatusCode/401" <PageTitle>Not Authorized</PageTitle> <h1>Not Authorized</h1> <p role="alert">Sorry, you are not authorized to access this page.</p>
5) تعاملی کردن سراسری برنامه
پس از این تغییرات اگر برنامه را اجرا کنید، بر اساس روش جدید static server-side rendering کار میکند و تعاملی نیست. یعنی تمام کامپوننتهای آن به صورت پیشفرض، یکبار بر روی سرور رندر شده و خروجی آنها به مرورگر کاربر ارسال میشوند و هیچ اتصال دائم SignalR ای برقرار نخواهد شد. برای فعالسازی سراسری قابلیتهای تعاملی برنامه و بازگشت به حالت Blazor Server قبلی، به فایل App.razor مراجعه کرده و دو تغییر زیر را اعمال کنید تا به صورت خودکار به تمام زیرکامپوننتها، یعنی کل برنامه، اعمال شود:
<HeadOutlet @rendermode="@InteractiveServer" /> ... <Routes @rendermode="@InteractiveServer" />
نکته 1: اجرای دستور زیر در داتنت 8، قالب پروژهای را ایجاد میکند که رفتار آن همانند پروژههای Blazor Server نگارشهای قبلی داتنت است (این مورد را در قسمت قبل بررسی کردیم)؛ یعنی همهجای آن به صورت پیشفرض، تعاملی است:
dotnet new blazor --interactivity Server --all-interactive
نکته 2: البته ... InteractiveServer، دقیقا همان حالت پیشفرض برنامههای Blazor Server قبلی نیست! این حالت رندر، به صورت پیشفرض به همراه پیشرندر (pre-rendering) هم هست. یعنی در این حالت، روال رویدادگردان OnInitializedAsync یک کامپوننت، دوبار فراخوانی میشود (که باید به آن دقت داشت و عدم توجه به آن میتواند سبب انجام دوبارهی کارهای سنگین آغازین یک کامپوننت شود)؛ یکبار برای پیشرندر صفحه به صورت یک HTML استاتیک (بدون فعال سازی هیچ قابلیت تعاملی) که برای موتورهای جستجو و بهبود SEO مفید است و بار دیگر برای فعالسازی قسمتهای تعاملی آن، درست پس از زمانیکه اتصال SignalR صفحه، برقرار شد (البته امکان فعالسازی حالت پیشرندر در Blazor Server قبلی هم وجود داشت؛ ولی مانند Blazor 8x، به صورت پیشفرض فعال نبود). در صورت نیاز، برای سفارشی سازی و لغو آن میتوان به صورت زیر عمل کرد:
@rendermode InteractiveServerRenderModeWithoutPrerendering @code{ static readonly IComponentRenderMode InteractiveServerRenderModeWithoutPrerendering = new InteractiveServerRenderMode(false); }
در مورد پیشرندر و روش مدیریت دوبار فراخوانی شدن روال رویدادگردان OnInitializedAsync یک کامپوننت در این حالت، در قسمتهای بعدی این سری بیشتر بحث خواهد شد.