ASP.NET Core 8x به همراه یک IResult جدید بهنام RazorComponentResult است که توسط آن میتوان در Endpointهای Minimal-API و همچنین اکشن متدهای MVC، از کامپوننتهای Blazor، خروجی گرفت. این خروجی نه فقط static یا به عبارتی SSR، بلکه حتی میتواند تعاملی هم باشد. در این مطلب، جزئیات فعالسازی و استفاده از این IResult جدید را در یک برنامهی Minimal-API بررسی میکنیم.
ایجاد یک برنامهی Minimal-API جدید در دات نت 8
پروژهای را که در اینجا پیگیری میکنیم، بر اساس قالب استاندارد تولید شدهی توسط دستور dotnet new webapi تکمیل میشود.
ایجاد یک صفحهی Blazor 8x به همراه مسیریابی و دریافت پارامتر
در ادامه قصد داریم که یک کامپوننت جدید را به نام SsrTest.razor در پوشهی جدید Components\Tests ایجاد کرده و برای آن مسیریابی از نوع page@ هم تعریف کنیم. یعنی نهفقط قصد داریم آنرا توسط RazorComponentResult رندر کنیم، بلکه میخواهیم اگر آدرس آنرا در مرورگر هم وارد کردیم، قابل دسترسی باشد.
به همین جهت یک پوشهی جدید را به نام Components در ریشهی پروژهی Web API جاری ایجاد میکنیم، با این محتوا:
برای ایده گرفتن از محتوای مورد نیاز، به «معرفی قالبهای جدید شروع پروژههای Blazor در دات نت 8» قسمت دوم این سری مراجعه کرده و برای مثال قالب سادهترین حالت ممکن را توسط دستور زیر تولید میکنیم (در یک پروژهی مجزا، خارج از پروژهی جاری):
پس از اینکار، محتویات پوشهی Components آنرا مستقیما داخل پوشهی پروژهی Minimal-API جاری کپی میکنیم. یعنی در نهایت در این پروژهی جدید Web API، به فایلهای زیر میرسیم:
- فایل Imports.razor_ ساده شده برای سهولت کار با فضاهای نام در کامپوننتهای Blazor (فضاهای نامی را که در آن وجود ندارند و مرتبط با پروژهی دوم هستند، حذف میکنیم).
- فایل App.razor، برای تشکیل نقطهی آغازین برنامهی Blazor.
- فایل Routes.razor برای معرفی مسیریابی صفحات Blazor تعریف شده.
- پوشهی Layout برای معرفی فایل MainLayout.razor که در Routes.razor استفاده شدهاست.
و ... یک فایل آزمایشی جدید به نام Components\Tests\SsrTest.razor با محتوای زیر:
این فایل، میتواند پارامتر Data را از طریق فراخوانی مستقیم آدرس فرضی http://localhost:5227/ssr-page/2 دریافت کند و یا ... از طریق خروجی جدید RazorComponentResult که توسط یک Endpoint سفارشی ارائه میشود:
تغییرات مورد نیاز در فایل Program.cs برنامهی Web-API برای فعالسازی رندر سمت سرور Blazor
در ادامه کل تغییرات مورد نیاز جهت اجرای این برنامه را مشاهده میکنید:
توضیحات:
- همین اندازه تغییر در جهت فعالسازی رندر سمت سرور کامپوننتهای Blazor در یک برنامهی ASP.NET Core کفایت میکند. یعنی اضافه شدن:
AddRazorComponents ،UseAntiforgery و MapRazorComponents
- در اینجا نحوهی ارسال پارامترها را به یک RazorComponentResult نیز مشاهده میکنید.
- در حالت فراخوانی از طریق مسیر endpoint (یعنی فراخوانی مسیر http://localhost:5227/ssr-component در مثال فوق)، خود کامپوننت فراخوانی شده، بدون layout تعریف شدهی در فایل App.razor، رندر میشود. علت اینجا است که layout برنامه به همراه کامپوننت Router و RouteView آن فعال میشود که این دو هم مختص به صفحات دارای مسیریابی Blazor هستند و برای رندر کامپوننتهای خالص آن بکار گرفته نمیشوند. خروجی RazorComponentResult تنها یک static SSR خالص است؛ مگر اینکه فایل blazor.web.js را نیز بارگذاری کند.
یک نکته: اگر در حالت رندر توسط RazorComponentResult، علاقمند به استفادهی از layout هستید، میتوان از کامپوننت LayoutView داخل یک کامپوننت فرضی به صورت زیر استفاده کرد؛ اما این مورد هم شامل اطلاعات فایل App.razor نمیشود:
سؤال: آیا در این حالت کامپوننتهای تعاملی هم کار میکنند؟
پاسخ: بله. فقط برای ایده گرفتن، یک نمونه پروژهی تعاملی Blazor 8x را در ابتدا ایجاد کنید و قسمتهای اضافی AddRazorComponents و MapRazorComponents آنرا در اینجا کپی کنید؛ یعنی برای مثال جهت فعالسازی کامپوننتهای تعاملی Blazor Server، به این دو تغییر زیر نیاز است:
همچنین باید دقت داشت که امکانات تعاملی، به دلیل وجود و دسترسی به یک سطر ذیل که در فایل Components\App.razor واقع شده، اجرایی میشوند:
و همانطور که عنوان شد، اگر از روش new RazorComponentResult استفاده میشود، باید این سطر را به صورت دستی اضافهکرد؛ چون به همراه رندر layout تعریف شدهی در فایل App.razor نیست. برای مثال فرض کنید کامپوننت معروف Counter را به صورت زیر داریم که حالت رندر آن به InteractiveServer تنظیم شدهاست:
در این حالت پس از تعریف endpoint زیر، خروجی آن فقط یک صفحهی استاتیک SSR خواهد بود و دکمهی Click me آن کار نمیکند:
علت اینجا است که اگر به سورس HTML رندر شده مراجعه کنیم، خبری از درج اسکریپت blazor.web.js در انتهای آن نیست. به همین جهت برای مثال فایل جدید CounterInteractive.razor را به صورت زیر اضافه میکنیم که ساختار آن شبیه به فایل App.razor است:
و هدف اصلی از آن، تشکیل یک قالب و درج اسکریپت blazor.web.js در انتهای آن است.
سپس تعریف endpoint متناظر را به صورت زیر تغییر میدهیم:
اینبار به علت بارگذاری فایل blazor.web.js، امکانات تعاملی کامپوننت Counter فعال شده و قابل استفاده میشوند.
سؤال: آیا میتوان این خروجی static SSR کامپوننتهای بلیزر را در سرویسهای یک برنامه ASP.NET Core هم دریافت کرد؟
منظور این است که آیا میتوان از یک کامپوننت Blazor، به همراه تمام پیشرفتهای Razor در آن که در Viewهای MVC قابل دسترسی نیستند، بهشکل یک رشتهی خالص، خروجی گرفت و برای مثال از آن بهعنوان قالب پویای محتوای ایمیلها استفاده کرد؟
پاسخ: بله! زیر ساخت RazorComponentResult که از سرویس HtmlRenderer استفاده میکند، بدون نیاز به برپایی یک endpoint هم قابل دسترسی است:
برای کار با آن، ابتدا باید سرویس فوق را به صورت زیر ثبت و معرفی کرد:
و سپس یک نمونه مثال فرضی نحوهی تزریق و فراخوانی سرویس BlazorStaticRendererService به صورت زیر است که در آن روش ارسال پارامترها هم بررسی شدهاست:
کدهای کامل این مطلب را میتوانید از اینجا دریافت کنید: WebApi8x.zip
ایجاد یک برنامهی Minimal-API جدید در دات نت 8
پروژهای را که در اینجا پیگیری میکنیم، بر اساس قالب استاندارد تولید شدهی توسط دستور dotnet new webapi تکمیل میشود.
ایجاد یک صفحهی Blazor 8x به همراه مسیریابی و دریافت پارامتر
در ادامه قصد داریم که یک کامپوننت جدید را به نام SsrTest.razor در پوشهی جدید Components\Tests ایجاد کرده و برای آن مسیریابی از نوع page@ هم تعریف کنیم. یعنی نهفقط قصد داریم آنرا توسط RazorComponentResult رندر کنیم، بلکه میخواهیم اگر آدرس آنرا در مرورگر هم وارد کردیم، قابل دسترسی باشد.
به همین جهت یک پوشهی جدید را به نام Components در ریشهی پروژهی Web API جاری ایجاد میکنیم، با این محتوا:
برای ایده گرفتن از محتوای مورد نیاز، به «معرفی قالبهای جدید شروع پروژههای Blazor در دات نت 8» قسمت دوم این سری مراجعه کرده و برای مثال قالب سادهترین حالت ممکن را توسط دستور زیر تولید میکنیم (در یک پروژهی مجزا، خارج از پروژهی جاری):
dotnet new blazor --interactivity None
- فایل Imports.razor_ ساده شده برای سهولت کار با فضاهای نام در کامپوننتهای Blazor (فضاهای نامی را که در آن وجود ندارند و مرتبط با پروژهی دوم هستند، حذف میکنیم).
- فایل App.razor، برای تشکیل نقطهی آغازین برنامهی Blazor.
- فایل Routes.razor برای معرفی مسیریابی صفحات Blazor تعریف شده.
- پوشهی Layout برای معرفی فایل MainLayout.razor که در Routes.razor استفاده شدهاست.
و ... یک فایل آزمایشی جدید به نام Components\Tests\SsrTest.razor با محتوای زیر:
@page "/ssr-page/{Data:int}" <PageTitle>An SSR component</PageTitle> <h1>An SSR component rendered by a Minimal-API!</h1> <div> Data: @Data </div> @code { [Parameter] public int Data { get; set; } }
تغییرات مورد نیاز در فایل Program.cs برنامهی Web-API برای فعالسازی رندر سمت سرور Blazor
در ادامه کل تغییرات مورد نیاز جهت اجرای این برنامه را مشاهده میکنید:
var builder = WebApplication.CreateBuilder(args); // ... builder.Services.AddRazorComponents(); // ... // http://localhost:5227/ssr-component?data=2 // or it can be called directly http://localhost:5227/ssr-page/2 app.MapGet("/ssr-component", (int data = 1) => { var parameters = new Dictionary<string, object?> { { nameof(SsrTest.Data), data }, }; return new RazorComponentResult<SsrTest>(parameters); }); app.UseStaticFiles(); app.UseAntiforgery(); app.MapRazorComponents<App>(); app.Run(); // ...
- همین اندازه تغییر در جهت فعالسازی رندر سمت سرور کامپوننتهای Blazor در یک برنامهی ASP.NET Core کفایت میکند. یعنی اضافه شدن:
AddRazorComponents ،UseAntiforgery و MapRazorComponents
- در اینجا نحوهی ارسال پارامترها را به یک RazorComponentResult نیز مشاهده میکنید.
- در حالت فراخوانی از طریق مسیر endpoint (یعنی فراخوانی مسیر http://localhost:5227/ssr-component در مثال فوق)، خود کامپوننت فراخوانی شده، بدون layout تعریف شدهی در فایل App.razor، رندر میشود. علت اینجا است که layout برنامه به همراه کامپوننت Router و RouteView آن فعال میشود که این دو هم مختص به صفحات دارای مسیریابی Blazor هستند و برای رندر کامپوننتهای خالص آن بکار گرفته نمیشوند. خروجی RazorComponentResult تنها یک static SSR خالص است؛ مگر اینکه فایل blazor.web.js را نیز بارگذاری کند.
یک نکته: اگر در حالت رندر توسط RazorComponentResult، علاقمند به استفادهی از layout هستید، میتوان از کامپوننت LayoutView داخل یک کامپوننت فرضی به صورت زیر استفاده کرد؛ اما این مورد هم شامل اطلاعات فایل App.razor نمیشود:
<LayoutView Layout="@typeof(MainLayout)"> <PageTitle>Home</PageTitle> <h2>Welcome to your new app.</h2> </LayoutView>
سؤال: آیا در این حالت کامپوننتهای تعاملی هم کار میکنند؟
پاسخ: بله. فقط برای ایده گرفتن، یک نمونه پروژهی تعاملی Blazor 8x را در ابتدا ایجاد کنید و قسمتهای اضافی AddRazorComponents و MapRazorComponents آنرا در اینجا کپی کنید؛ یعنی برای مثال جهت فعالسازی کامپوننتهای تعاملی Blazor Server، به این دو تغییر زیر نیاز است:
// ... builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); // ... app.MapRazorComponents<App>().AddInteractiveServerRenderMode(); // ...
<script src="_framework/blazor.web.js"></script>
@rendermode InteractiveServer <h1>Counter</h1> <p role="status">Current count: @_currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { private int _currentCount; private void IncrementCount() { _currentCount++; } }
// http://localhost:5227/server-interactive-component app.MapGet("/server-interactive-component", () => new RazorComponentResult<Counter>());
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Interactive server component</title> <base href="/"/> </head> <body> <h1>Interactive server component</h1> <Counter/> <script src="_framework/blazor.web.js"></script> </body> </html>
سپس تعریف endpoint متناظر را به صورت زیر تغییر میدهیم:
// http://localhost:5227/server-interactive-component app.MapGet("/server-interactive-component", () => new RazorComponentResult<CounterInteractive>());
سؤال: آیا میتوان این خروجی static SSR کامپوننتهای بلیزر را در سرویسهای یک برنامه ASP.NET Core هم دریافت کرد؟
منظور این است که آیا میتوان از یک کامپوننت Blazor، به همراه تمام پیشرفتهای Razor در آن که در Viewهای MVC قابل دسترسی نیستند، بهشکل یک رشتهی خالص، خروجی گرفت و برای مثال از آن بهعنوان قالب پویای محتوای ایمیلها استفاده کرد؟
پاسخ: بله! زیر ساخت RazorComponentResult که از سرویس HtmlRenderer استفاده میکند، بدون نیاز به برپایی یک endpoint هم قابل دسترسی است:
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; namespace WebApi8x.Services; public class BlazorStaticRendererService { private readonly HtmlRenderer _htmlRenderer; public BlazorStaticRendererService(HtmlRenderer htmlRenderer) => _htmlRenderer = htmlRenderer; public Task<string> StaticRenderComponentAsync<T>() where T : IComponent => RenderComponentAsync<T>(ParameterView.Empty); public Task<string> StaticRenderComponentAsync<T>(Dictionary<string, object?> dictionary) where T : IComponent => RenderComponentAsync<T>(ParameterView.FromDictionary(dictionary)); private Task<string> RenderComponentAsync<T>(ParameterView parameters) where T : IComponent => _htmlRenderer.Dispatcher.InvokeAsync(async () => { var output = await _htmlRenderer.RenderComponentAsync<T>(parameters); return output.ToHtmlString(); }); }
builder.Services.AddScoped<HtmlRenderer>(); builder.Services.AddScoped<BlazorStaticRendererService>();
app.MapGet("/static-renderer-service-test", async (BlazorStaticRendererService renderer, int data = 1) => { var parameters = new Dictionary<string, object?> { { nameof(SsrTest.Data), data }, }; var html = await renderer.StaticRenderComponentAsync<SsrTest>(parameters); return Results.Content(html, "text/html"); });
کدهای کامل این مطلب را میتوانید از اینجا دریافت کنید: WebApi8x.zip