2019年12月23日 星期一

ASP.NET Core Blazor 使用網站跟目錄與其他目錄的靜態圖片使用練習

ASP.NET Core Blazor 使用網站跟目錄與其他目錄的靜態圖片使用練習

更多關於 Blazor 教學影片,可以參考 Blazor 教學影片播放清單 或者 Blazor 快速體驗教學影片撥放清單。也歡迎訂閱本 .NET / Blazor / Xamarin.Forms 影片頻道 。


在這篇文章中,將要來練習如何在 Blazor 專案內,顯示專案中的靜態圖片檔案;這些靜態的圖片檔案可以存在於該專案的網站根目錄下,也可以另外建立一個方案資料夾,宣告這個這料夾內可以儲存任何網站的靜態檔案,這也包括了圖片檔案。
在這篇文章所提到的專案原始碼,可以從 GitHub 下載

使用預設專案範本建立 Blazor 專案

想要進行這樣的專案開發練習,可以參考底下的操作步驟
  • 打開 Visual Studio 2019 開發工具
  • 當 [Visual Studio 2019] 對話窗出現之後,點選右下方的 [建立新的專案] 按鈕
  • 在 [建立新專案] 對話窗內,請找出 [Blazor 應用程式] 這個專案開發範本,並且點選這個專案開發範本
  • 請點選右下角 [下一步] 按鈕
  • 出現 [設定新的專案] 對話窗,輸入適當的 [專案名稱] 、 [位置] ,完成後,請點選右下角 [建立] 按鈕
    在這個範例程式碼中,將會建立一個 BlazorOutsideImage 專案名稱
  • 此時將會看到 [建立新的 Blazor 應用程式] 對話窗,這裡可以根據當時開發專案的需要,自行決定是否有調整 Blazor 專案的其他特性,若無,請點選右下角的 [建立] 按鈕
  • 此時,這個 Blazor 專案已經建立完成

建立相關方案資料夾與複製圖片檔案

現在,將會準備三個圖片檔案: blazor-webassembly.png 、 blazor-server.png 、 JavaScriptInterop.png,完成後的結果將會如同下面螢幕截圖。
  • 滑鼠右擊這個專案節點
  • 選擇 [加入] > [新增資料夾] 選項
  • 使用 StaticFilesFolder 名稱作為該方案資料夾的名稱
  • 滑鼠右擊 [wwwroot] 這個特殊資料夾
  • 選擇 [加入] > [新增資料夾] 選項
  • 使用 Images 名稱作為該方案資料夾的名稱
  • 接著,將圖片檔案 blazor-webassembly.png 複製到 [wwwroot] > [Images] 資料夾內
  • 將圖片檔案 blazor-server.png 複製到 [wwwroot] 資料夾內
  • 將圖片檔案 JavaScriptInterop.png 複製到 [StaticFilesFolder] 目錄下
  • 記得要將 JavaScriptInterop.png 這個圖片檔案,在方案總管的屬性視窗內,宣告 [建置動作] 欄位的值為 [永遠複製]
    若沒有宣告相關圖片檔案的 [建置動作] 欄位的值為 [永遠複製],則將無法在網頁上看到這些圖片

修正 Startup.cs

  • 請打開 Startup.cs 這個檔案
  • 找到 Configure
  • 在 app.UseStaticFiles(); 敘述下面,加入這段程式碼
    // 這裡加入底下 Middleware 中介軟體 宣告,在這個專案內新增一個檔案提供者指向 /StaticFiles 目錄
    app.UseStaticFiles(new StaticFileOptions()
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "StaticFilesFolder")),
        RequestPath = "/StaticFiles"
    });
現在,這個專案可以使用這個 URL /StaticFiles 來指向任何在 [StaticFilesFolder] 方案資料夾內的靜態檔案了
底下的程式碼將會是完成的結果
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    // 這行不能刪除,因為這會指向專案內的 wwwroot 目錄下
    app.UseStaticFiles();
    // 這裡加入底下 Middleware 中介軟體 宣告,在這個專案內新增一個檔案提供者指向 /StaticFiles 目錄
    app.UseStaticFiles(new StaticFileOptions()
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "StaticFilesFolder")),
        RequestPath = "/StaticFiles"
    });

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
    });
}

關於 ASP.NET Core 中的檔案提供者

ASP.NET Core 透過使用檔案提供者,將檔案系統存取抽象化,可以參考 關於 ASP.NET Core 中的檔案提供者 得到更多詳細說明

修改 Index.razor Blazor 元件

現在,要在 Index.razor 元件中,來顯示這些靜態圖片
  • 在 Pages 資料夾內找到 Index.razor 檔案
  • 打開這個檔案,使用底下程式碼進行替換
@page "/"

<h1>Hello, world!</h1>

<h2>網站根目錄 /blazor-server.png</h2>
<div>
    <img src="/blazor-server.png" />
</div>

<h2>網站根目錄下的目錄 /Images/blazor-webassembly.png</h2>
<div>
    <img src="/Images/blazor-webassembly.png" />
</div>

<h2>非網站根目錄,這裡是另外宣告的 StaticFilesFolder 目錄 /StaticFiles/JavaScriptInterop.png</h2>
<div>
    <img src="/StaticFiles/JavaScriptInterop.png" />
</div>
@code
{

}

查看執行結果

好的,可以來執行這個專案
當這個 Blazor 網站跑起來之後,就會看到如上面螢幕截圖,這三個圖片都正常顯示在網頁上了。



2019年12月20日 星期五

在 ASP.NET Core Blazor 專案編譯後的 .cshtml & .razor 變成甚麼呢?

在 ASP.NET Core Blazor 專案編譯後的 .cshtml & .razor 變成甚麼呢?

更多關於 Blazor 教學影片,可以參考 Blazor 教學影片播放清單 或者 Blazor 快速體驗教學影片撥放清單。也歡迎訂閱本 .NET / Blazor / Xamarin.Forms 影片頻道 。


當設計好 Blazor 專案之後,經過建置、編譯之後,原先的 .cshtml & .razor 會變成甚麼內容呢?原則上,不論剛剛提到的兩個類型檔案,都會變成 c# 的類別。
現在,可以建立一個伺服器端的 Blazor 專案,並且建置成功之後,接著,可以使用任何一套 .NET 組件 Assembly 的反组譯工具,開啟這個 Blazor 專案的組件,就可以看到原先的 Counter.razor 原始碼為
@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
從編譯之後的組件內,會看到在 Pages 命名空間內有個 Counter 節點,點選這個節點,就會看到編譯後的 Counter.razor 所產生的類別
// BlazorApp10.Pages.Counter
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.Web;
using System;

[Route("/counter")]
public class Counter : ComponentBase
{
    private int currentCount = 0;

    protected override void BuildRenderTree(RenderTreeBuilder __builder)
    {
        __builder.AddMarkupContent(0, "<h1>Counter</h1>\r\n\r\n");
        __builder.OpenElement(1, "p");
        __builder.AddContent(2, "Current count: ");
        __builder.AddContent(3, currentCount);
        __builder.CloseElement();
        __builder.AddMarkupContent(4, "\r\n\r\n");
        __builder.OpenElement(5, "button");
        __builder.AddAttribute(6, "class", "btn btn-primary");
        __builder.AddAttribute(7, "onclick", EventCallback.Factory.Create<MouseEventArgs>((object)this, (Action)IncrementCount));
        __builder.AddContent(8, "Click me");
        __builder.CloseElement();
    }

    private void IncrementCount()
    {
        currentCount++;
    }
}
這裡產生了一個 Counter 類別,並且是繼承了 ComponentBase 類別,而且其中,宣告在 .razor 檔案內的 HTML 標記宣告內容,將會由 BuildRenderTree 方法重新呈現出來;而對於 @code 區段內的程式碼,則就會存在於該類別內。
現在來看看 _Host.cshtml,在 Blazor 預設專案範本建立的時候,會呈現如下的標記宣告內容
@page "/"
@namespace BlazorApp10.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BlazorApp10</title>
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
</head>
<body>
    <app>
        <component type="typeof(App)" render-mode="ServerPrerendered" />
    </app>

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>
上面的檔案在編譯之後,將會出現在另外一個組件上
在該組件內會有一個 Pages__Host 類別,該類別繼承了 Page 類別,底下就是這個 Razor Page (Razor 頁面) 經過編譯器產生的類別原始碼
// BlazorApp10.Pages.Pages__Host
using BlazorApp10;
using BlazorApp10.Pages;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.Hosting;
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

[RazorCompiledItemMetadata("RouteTemplate", "/")]
[RazorSourceChecksum("SHA1", "c4eef6e21b532050217466a050cf103768022b7b", "/Pages/_Host.cshtml")]
public class Pages__Host : Page
{
    private static readonly TagHelperAttribute __tagHelperAttribute_0 = (TagHelperAttribute)(object)new TagHelperAttribute("href", (object)new HtmlString("~/"), (HtmlAttributeValueStyle)0);

    private static readonly TagHelperAttribute __tagHelperAttribute_1 = (TagHelperAttribute)(object)new TagHelperAttribute("include", (object)"Staging,Production", (HtmlAttributeValueStyle)0);

    private static readonly TagHelperAttribute __tagHelperAttribute_2 = (TagHelperAttribute)(object)new TagHelperAttribute("include", (object)"Development", (HtmlAttributeValueStyle)0);

    private TagHelperExecutionContext __tagHelperExecutionContext;

    private TagHelperRunner __tagHelperRunner = (TagHelperRunner)(object)new TagHelperRunner();

    private string __tagHelperStringValueBuffer;

    private TagHelperScopeManager __backed__tagHelperScopeManager = null;

    private HeadTagHelper __Microsoft_AspNetCore_Mvc_Razor_TagHelpers_HeadTagHelper;

    private UrlResolutionTagHelper __Microsoft_AspNetCore_Mvc_Razor_TagHelpers_UrlResolutionTagHelper;

    private BodyTagHelper __Microsoft_AspNetCore_Mvc_Razor_TagHelpers_BodyTagHelper;

    private ComponentTagHelper __Microsoft_AspNetCore_Mvc_TagHelpers_ComponentTagHelper;

    private EnvironmentTagHelper __Microsoft_AspNetCore_Mvc_TagHelpers_EnvironmentTagHelper;

    private TagHelperScopeManager __tagHelperScopeManager
    {
        get
        {
            //IL_0028: Unknown result type (might be due to invalid IL or missing references)
            //IL_0032: Expected O, but got Unknown
            if (__backed__tagHelperScopeManager == null)
            {
                __backed__tagHelperScopeManager = (TagHelperScopeManager)(object)new TagHelperScopeManager((Action<HtmlEncoder>)base.StartTagHelperWritingScope, (Func<TagHelperContent>)base.EndTagHelperWritingScope);
            }
            return __backed__tagHelperScopeManager;
        }
    }

    [RazorInject]
    public IModelExpressionProvider ModelExpressionProvider
    {
        get;
        private set;
    }

    [RazorInject]
    public IUrlHelper Url
    {
        get;
        private set;
    }

    [RazorInject]
    public IViewComponentHelper Component
    {
        get;
        private set;
    }

    [RazorInject]
    public IJsonHelper Json
    {
        get;
        private set;
    }

    [RazorInject]
    public IHtmlHelper<Pages__Host> Html
    {
        get;
        private set;
    }

    public ViewDataDictionary<Pages__Host> ViewData => (ViewDataDictionary<Pages__Host>)(object)base.PageContext?.ViewData;

    public Pages__Host Model => ViewData.get_Model();

    public override async Task ExecuteAsync()
    {
        ((RazorPageBase)this).set_Layout((string)null);
        ((RazorPageBase)this).WriteLiteral("\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n");
        __tagHelperExecutionContext = __tagHelperScopeManager.Begin("head", (TagMode)0, "c4eef6e21b532050217466a050cf103768022b7b4361", (Func<Task>)async delegate
        {
            ((RazorPageBase)this).WriteLiteral("\r\n    <meta charset=\"utf-8\" />\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\r\n    <title>BlazorApp10</title>\r\n    ");
            __tagHelperExecutionContext = __tagHelperScopeManager.Begin("base", (TagMode)1, "c4eef6e21b532050217466a050cf103768022b7b4775", (Func<Task>)async delegate
            {
            });
            __Microsoft_AspNetCore_Mvc_Razor_TagHelpers_UrlResolutionTagHelper = ((RazorPageBase)this).CreateTagHelper<UrlResolutionTagHelper>();
            __tagHelperExecutionContext.Add((ITagHelper)(object)__Microsoft_AspNetCore_Mvc_Razor_TagHelpers_UrlResolutionTagHelper);
            __tagHelperExecutionContext.AddHtmlAttribute(__tagHelperAttribute_0);
            await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
            if (!__tagHelperExecutionContext.get_Output().get_IsContentModified())
            {
                await __tagHelperExecutionContext.SetOutputContentAsync();
            }
            ((RazorPageBase)this).Write((object)__tagHelperExecutionContext.get_Output());
            __tagHelperExecutionContext = __tagHelperScopeManager.End();
            ((RazorPageBase)this).WriteLiteral("\r\n    <link rel=\"stylesheet\" href=\"css/bootstrap/bootstrap.min.css\" />\r\n    <link href=\"css/site.css\" rel=\"stylesheet\" />\r\n");
        });
        __Microsoft_AspNetCore_Mvc_Razor_TagHelpers_HeadTagHelper = ((RazorPageBase)this).CreateTagHelper<HeadTagHelper>();
        __tagHelperExecutionContext.Add((ITagHelper)(object)__Microsoft_AspNetCore_Mvc_Razor_TagHelpers_HeadTagHelper);
        await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
        if (!__tagHelperExecutionContext.get_Output().get_IsContentModified())
        {
            await __tagHelperExecutionContext.SetOutputContentAsync();
        }
        ((RazorPageBase)this).Write((object)__tagHelperExecutionContext.get_Output());
        __tagHelperExecutionContext = __tagHelperScopeManager.End();
        ((RazorPageBase)this).WriteLiteral("\r\n");
        __tagHelperExecutionContext = __tagHelperScopeManager.Begin("body", (TagMode)0, "c4eef6e21b532050217466a050cf103768022b7b6703", (Func<Task>)async delegate
        {
            ((RazorPageBase)this).WriteLiteral("\r\n    <app>\r\n        ");
            __tagHelperExecutionContext = __tagHelperScopeManager.Begin("component", (TagMode)1, "c4eef6e21b532050217466a050cf103768022b7b6982", (Func<Task>)async delegate
            {
            });
            __Microsoft_AspNetCore_Mvc_TagHelpers_ComponentTagHelper = ((RazorPageBase)this).CreateTagHelper<ComponentTagHelper>();
            __tagHelperExecutionContext.Add((ITagHelper)(object)__Microsoft_AspNetCore_Mvc_TagHelpers_ComponentTagHelper);
            __Microsoft_AspNetCore_Mvc_TagHelpers_ComponentTagHelper.set_ComponentType(typeof(App));
            __tagHelperExecutionContext.AddTagHelperAttribute("type", (object)__Microsoft_AspNetCore_Mvc_TagHelpers_ComponentTagHelper.get_ComponentType(), (HtmlAttributeValueStyle)0);
            __Microsoft_AspNetCore_Mvc_TagHelpers_ComponentTagHelper.set_RenderMode((RenderMode)3);
            __tagHelperExecutionContext.AddTagHelperAttribute("render-mode", (object)__Microsoft_AspNetCore_Mvc_TagHelpers_ComponentTagHelper.get_RenderMode(), (HtmlAttributeValueStyle)0);
            await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
            if (!__tagHelperExecutionContext.get_Output().get_IsContentModified())
            {
                await __tagHelperExecutionContext.SetOutputContentAsync();
            }
            ((RazorPageBase)this).Write((object)__tagHelperExecutionContext.get_Output());
            __tagHelperExecutionContext = __tagHelperScopeManager.End();
            ((RazorPageBase)this).WriteLiteral("\r\n    </app>\r\n\r\n    <div id=\"blazor-error-ui\">\r\n        ");
            __tagHelperExecutionContext = __tagHelperScopeManager.Begin("environment", (TagMode)0, "c4eef6e21b532050217466a050cf103768022b7b9004", (Func<Task>)async delegate
            {
                ((RazorPageBase)this).WriteLiteral("\r\n            An error has occurred. This application may no longer respond until reloaded.\r\n        ");
            });
            __Microsoft_AspNetCore_Mvc_TagHelpers_EnvironmentTagHelper = ((RazorPageBase)this).CreateTagHelper<EnvironmentTagHelper>();
            __tagHelperExecutionContext.Add((ITagHelper)(object)__Microsoft_AspNetCore_Mvc_TagHelpers_EnvironmentTagHelper);
            __Microsoft_AspNetCore_Mvc_TagHelpers_EnvironmentTagHelper.set_Include((string)__tagHelperAttribute_1.get_Value());
            __tagHelperExecutionContext.AddTagHelperAttribute(__tagHelperAttribute_1);
            await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
            if (!__tagHelperExecutionContext.get_Output().get_IsContentModified())
            {
                await __tagHelperExecutionContext.SetOutputContentAsync();
            }
            ((RazorPageBase)this).Write((object)__tagHelperExecutionContext.get_Output());
            __tagHelperExecutionContext = __tagHelperScopeManager.End();
            ((RazorPageBase)this).WriteLiteral("\r\n        ");
            __tagHelperExecutionContext = __tagHelperScopeManager.Begin("environment", (TagMode)0, "c4eef6e21b532050217466a050cf103768022b7b10361", (Func<Task>)async delegate
            {
                ((RazorPageBase)this).WriteLiteral("\r\n            An unhandled exception has occurred. See browser dev tools for details.\r\n        ");
            });
            __Microsoft_AspNetCore_Mvc_TagHelpers_EnvironmentTagHelper = ((RazorPageBase)this).CreateTagHelper<EnvironmentTagHelper>();
            __tagHelperExecutionContext.Add((ITagHelper)(object)__Microsoft_AspNetCore_Mvc_TagHelpers_EnvironmentTagHelper);
            __Microsoft_AspNetCore_Mvc_TagHelpers_EnvironmentTagHelper.set_Include((string)__tagHelperAttribute_2.get_Value());
            __tagHelperExecutionContext.AddTagHelperAttribute(__tagHelperAttribute_2);
            await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
            if (!__tagHelperExecutionContext.get_Output().get_IsContentModified())
            {
                await __tagHelperExecutionContext.SetOutputContentAsync();
            }
            ((RazorPageBase)this).Write((object)__tagHelperExecutionContext.get_Output());
            __tagHelperExecutionContext = __tagHelperScopeManager.End();
            ((RazorPageBase)this).WriteLiteral("\r\n        <a");
            ((RazorPageBase)this).BeginWriteAttribute("href", " href=\"", 931, "\"", 938, 0);
            ((RazorPageBase)this).EndWriteAttribute();
            ((RazorPageBase)this).WriteLiteral(" class=\"reload\">Reload</a>\r\n        <a class=\"dismiss\">\ud83d\uddd9</a>\r\n    </div>\r\n\r\n    <script src=\"_framework/blazor.server.js\"></script>\r\n");
        });
        __Microsoft_AspNetCore_Mvc_Razor_TagHelpers_BodyTagHelper = ((RazorPageBase)this).CreateTagHelper<BodyTagHelper>();
        __tagHelperExecutionContext.Add((ITagHelper)(object)__Microsoft_AspNetCore_Mvc_Razor_TagHelpers_BodyTagHelper);
        await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
        if (!__tagHelperExecutionContext.get_Output().get_IsContentModified())
        {
            await __tagHelperExecutionContext.SetOutputContentAsync();
        }
        ((RazorPageBase)this).Write((object)__tagHelperExecutionContext.get_Output());
        __tagHelperExecutionContext = __tagHelperScopeManager.End();
        ((RazorPageBase)this).WriteLiteral("\r\n</html>\r\n");
    }
}