2021年6月23日 星期三

如何使用 Ant Design Blazor

如何使用 Ant Design Blazor

Blazor 這個優異的 Web UI 開發框架,讓 .NET C# 開發者可以輕鬆容易地進行各種網頁專案開發,而且,網路上也存在著許多各種不同 UI 套件來滿足 Blazor 開發者的需求,在這篇文章中,將要來體驗這套 Ant Design of Blazor UI 套件,看看如何使用與執行出來的效果如何。

這篇文章的原始碼位於 bzAntDesign

建立 Blazor Server-Side 的專案

  • 開啟 Visual Studio 2019
  • 選擇右下方的 [建立新的專案] 按鈕
  • 在 [建立新專案] 對話窗中
  • 從右上方的專案類型下拉按鈕中,找到並選擇 [Web]
  • 從可用專案範本清單內,找到並選擇 [Blazor Server 應用程式]
  • 點選左下方 [下一步] 按鈕
  • 在 [設定新的專案] 對話窗中
  • 在 [專案名稱] 欄位中輸入 bzAntDesign
  • 點選左下方 [下一步] 按鈕
  • 在 [其他資訊] 對話窗中
  • 在 [目標 Framework] 下拉選單中,選擇 [.NET 5.0 (目前)]
  • 點選左下方 [建立] 按鈕

加入所需要使用到的 NuGet 套件

  • 滑鼠右擊 [bzAntDesign] 專案內的 [相依性] 節點
  • 從彈出功能表中,選擇 [管理 NuGet 套件]
  • 當 [NuGet: bzAntDesign] 視窗出現後,切換到 [瀏覽] 標籤頁次
  • 搜尋 [AntDesign] 並且安裝並且套件

安裝與設定 AntDesign 元件

  • 參考 Import Ant Design Blazor into an existing project 文件說明,準備開始進行這個元件的安裝與設定
  • 打開這個專案根目錄下的 [Startup.cs] 檔案
  • 找到這個方法 [ConfigureServices]
  • 在其方法內加入底下程式碼
#region 加入 Ant Design 會用到的相依性服務註冊
services.AddAntDesign();
#endregion
  • 打開 [Pages] 資料夾下的 [_Host.cshtml] 檔案
  • 在 <head> ... </head> 區段內加入底下 HTML 標記
@*加入 Ant Design 會用到的靜態參考*@
<link href="_content/AntDesign/css/ant-design-blazor.css" rel="stylesheet" />
<script src="_content/AntDesign/js/ant-design-blazor.js"></script>
  • 打開這個專案根目錄下的 [_Imports.razor] 檔案
  • 在其檔案的最後面加入底下程式碼
@using AntDesign
  • 打開這個專案根目錄下的 [App.razor] 檔案
  • 在其檔案的最後面加入底下程式碼
<AntContainer /> 

建立一個可以使用對話窗的元件

  • 滑鼠右擊 [Pages] 資料夾
  • 從彈出功能表點選 [加入] > [Razor 元件]
  • 在下方 [名稱] 欄位輸入 [DialogView.razor]
  • 點選 [新增] 按鈕
  • 使用底下程式碼替換這個檔案內容
@inject MessageService _message

<Button Type="primary" OnClick="@(()=>{ _visible = true; })">
    Open Modal
</Button>
<Modal Title="@title"
       Visible="@_visible"
       OkText="@okText"
       OnOk="@HandleOk"
       CancelText="@cancelText"
       OnCancel="@HandleCancel">
    <b>Select:</b><br />

    <Form Model="_formModal">
        <FormItem Label="类型">
            <RadioGroup @bind-Value="@context.Type">
                <Radio Value="1">Select</Radio>
                <Radio Value="2">None</Radio>
            </RadioGroup>
        </FormItem>
        @if (context.Type == 1)
        {
            <FormItem>
                <Select DataSource="@_persons"
                        @bind-Value="@context.SelectedValue"
                        LabelName="@nameof(Person.Name)"
                        ValueName="@nameof(Person.Id)"
                        Placeholder="Select a person"
                        Style="width:120px"
                        AllowClear>
                </Select>
            </FormItem>
        }
    </Form>
    <div style="margin: 64px" />

    <b>Dropdown:</b><br />
    <Dropdown>
        <Overlay>
            <Menu>
                <MenuItem>
                    <a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">
                        1st menu item
                    </a>
                </MenuItem>
            </Menu>
        </Overlay>
        <ChildContent>
            <a class="ant-dropdown-link" @onclick:preventDefault>
                Hover me <Icon Type="down" />
            </a>
        </ChildContent>
    </Dropdown>

    <div style="margin: 64px" />

    <b>DatePicker:</b><br />
    <DatePicker TValue="DateTime?" Picker="@DatePickerType.Date" />

    <div style="margin: 64px" />

    <b>Tooltip:</b><br />
    <Tooltip Title="@("prompt text")">
        <span>Tooltip will show on mouse enter.</span>
    </Tooltip>

    <div style="margin: 64px" />

    <b>Popconfirm:</b><br />
    <Popconfirm Title="Are you sure delete this task?"
                OkText="Yes"
                CancelText="No">
        <a>Delete</a>
    </Popconfirm>

</Modal>

@code{
    class FormModal
    {
        public string Name { get; set; }
        public int Type { get; set; }
        public int SelectedValue { get; set; }
    }

    class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    string okText = "確定";
    string cancelText = "取消";
    string title = "BasicModal";
    bool _visible = false;
    List<Person> _persons;

    RenderFragment _content =
    @<div>
        <p>Content</p>
        <p>Content</p>
    </div>;

private FormModal _formModal = new FormModal();

protected override void OnInitialized()
{
  _persons = new List<Person>
{
            new Person {Id = 1, Name = "Jack"},
            new Person {Id = 2, Name = "Lucy"},
            new Person {Id = 3, Name = "Yaoming"},
            new Person {Id = 4, Name = "Frieda"},
        };
}

private void HandleOk(MouseEventArgs e)
{
  Console.WriteLine(e);
  _visible = false;
}

private void HandleCancel(MouseEventArgs e)
{
  Console.WriteLine(e);
  _visible = false;
}
}

修正 Index.razor 頁面

  • 打開 [Pages] 資料夾下的 [Index.razor] 檔案
  • 使用底下程式碼替換掉這個檔案內容
@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.

<div>
    <Button >HTML button</Button>
    <Button Type="primary">primary button</Button>
    <Button Type="default">default button</Button>
    <Button Type="dashed">dashed button</Button>
    <Button Type="link">link button</Button>

</div>

<div class="my-2">
    <DialogView/>
</div>

<SurveyPrompt Title="How is Blazor working for you?" />

執行並且測試

  • 按下 F5 開始執行這個專案

  • 網頁出現之後,請點選左邊的 [Counter] 按鈕

  • 現在將會看到底下的畫面

    在這裡可可以看到 Ant Design 這套元件提供的不同按鈕樣貌

  • 接著,點選 [Open Modal] 這個按鈕,則會看到一個對話窗出現在網頁上

 










2021年6月22日 星期二

ASP.NET Core 應該具備知識 - 中介軟體

ASP.NET Core 應該具備知識 - 中介軟體



ASP.NET Core 應該具備知識

ASP.NET Core 應用程式啟動與 Startup 類別

ASP.NET Core 靜態檔案 UseStaticFiles

ASP.NET Core 相依性注入設計模式 Dependency Injection

ASP.NET Core 中介軟體 Middleware

ASP.NET Core 設定 Configuration

ASP.NET Core 選項模式 IOptions

ASP.NET Core 日誌記錄 Logger

ASP.NET Core 從空白專案建立 Blazor 應用

建立一個 空白 ASP.NET Core 專案

  • 開啟 Visual Studio 2019

  • 在 [Visual Studio 2019] 對話窗中,點選右下方的 [建立新的專案] 選項

  • 在 [建立新專案] 對話窗中,在中間上方的專案範本過濾條件中

    1. 設定程式語言為 [C#]
    2. 設定專案範本為 [Web]
    3. 選擇專案範本項目清單,點選 [ASP.NET Core Web 應用程式 (Model-View-Controller)] 這個專案範本項目
    4. 點選右下方的 [下一步] 按鈕
  • 在 [設定新的專案] 對話窗出現後

    在 [專案名稱] 內,輸入 AC04

    點選右下角的 [下一步] 按鈕

  • 在 [其他資訊] 對話窗出現後,確認 [目標 Framework] 的下拉選單要選擇 [.NET 5.0 (目前)]

  • 點選右下角的 [建立] 按鈕

  • 此時這個 [ASP.NET Core Web 應用程式 (Model-View-Controller)] 專案已經建立完成,從方案總管視窗內可以看到如下圖的結構

  • 請打開根目錄下的 [Startup.cs] 檔案,中介軟體將會在這裡進行設計

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AC04
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/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();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

加入一個客製化的圖片資料夾與圖片檔案

  • 滑鼠右擊專案節點

  • 從彈出功能表中選取 [加入] > [新增資料夾]

  • 命名這個新資料夾為 [MyImages]

  • 請自行找到一個圖片檔案

  • 在這裡將會使用一個 [me.jpg] 圖片檔案

  • 使用檔案總管拖拉這個檔案到 [MyImages] 資料夾內

  • 滑鼠右擊這個 [me.jpg] 圖片檔案

  • 從彈出功能表點選 [屬性] 選項

  • 從 [屬性] 視窗,設定 [複製到輸出目錄] 項目為 [有更新時才複製]

加入客製化的中介軟體

  • 在根目錄下找到 [Startup.cs] 檔案
  • 打開 [Startup.cs] 檔案並且搜尋 [Configure] 方法
  • 在該方法的第一行,插入底下程式碼
#region Case 1 自行客製中介軟體 1
app.Use(async (context, next) =>
{
    // 呼叫下一個中介軟體要執行的內容,在這裡不要寫入任何內容到 HTTP Responet 回應內
    Console.WriteLine($"呼叫第 1 個客製化中介軟體");
    Console.WriteLine($"路由路徑 {context.Request.Path}");
    await next.Invoke();
    // 自這裡進行寫入日誌或其他工作,在這裡不要寫入任何內容到 HTTP Responet 回應內
    Console.WriteLine($"結束執行第 1 個客製化中介軟體");
    Console.WriteLine($"回應狀態碼 {context.Response.StatusCode}");
    Console.WriteLine();
});
#endregion

執行這個專案 - Case1 中介軟體

  • 為了做出更加詳細的測試,請先將瀏覽器的快取內容

  • 這裡使用的是 微軟 Chrome 瀏覽器

  • 點選瀏覽器右上方的 ...

  • 在彈出功能表選出 [設定] 選項

  • 在 [設定] 網頁上,點選左方功能清單中的 [隱私權、搜尋與服務] 項目

  • 在右方的網頁中,找到 [清除瀏覽資料] > [立即清除瀏覽資料] 右方的 [選擇要清除的項目]

  • 此時看到 [清除瀏覽資料] 對話窗

  • 依照預設值設定內容,點選下方的 [立即清除] 按鈕

  • 稍微等候一下,相關的快取檔案就會清除了

  • 請切換使用 Kestrol Web 伺服器模式,按下 [F5] 按鍵,開始執行這個專案

  • 瀏覽器的畫面如下

  • 當開啟了 [https://localhost:5001/] 網址之後,可以在 [命令提示字元視窗] 看到底下的內容

呼叫第 1 個客製化中介軟體
路由路徑 /
結束執行第 1 個客製化中介軟體
回應狀態碼 200

呼叫第 1 個客製化中介軟體
呼叫第 1 個客製化中介軟體
呼叫第 1 個客製化中介軟體
路由路徑 /js/site.js
路由路徑 /css/site.css
呼叫第 1 個客製化中介軟體
路由路徑 /lib/jquery/dist/jquery.min.js
路由路徑 /lib/bootstrap/dist/css/bootstrap.min.css
呼叫第 1 個客製化中介軟體
路由路徑 /lib/bootstrap/dist/js/bootstrap.bundle.min.js
結束執行第 1 個客製化中介軟體
結束執行第 1 個客製化中介軟體
回應狀態碼 200

回應狀態碼 200

結束執行第 1 個客製化中介軟體
結束執行第 1 個客製化中介軟體
結束執行第 1 個客製化中介軟體
回應狀態碼 200

回應狀態碼 200

回應狀態碼 200

呼叫第 1 個客製化中介軟體
路由路徑 /favicon.ico
結束執行第 1 個客製化中介軟體
回應狀態碼 200
  • 從上面的內容可以看出,當開啟一個 ASP.NET MVC 網頁的時候,並不是只有一個 HTTP Request 請求對 Web 伺服器上來執行

  • 這裡會有 路由路徑 / 、 路由路徑 /js/site.js 、 路由路徑 /css/site.css 、 路由路徑 /lib/jquery/dist/jquery.min.js 、 路由路徑 /lib/bootstrap/dist/css/bootstrap.min.css 、 路由路徑 /lib/bootstrap/dist/js/bootstrap.bundle.min.js 、 路由路徑 /favicon.ico 7 個 HTTP Request 請求,因為為了要顯示這個網頁,需要從 Web 伺服器讀取相關的 JavaScript 檔案,以便可以在瀏覽器端執行

  • 現在可以按下 [Shift] + [F5] 按鍵,停止這個專案執行

  • 接著再度按下 [F5] 按鍵,開始執行這個專案

  • 當瀏覽器顯示之後,當開啟了 [https://localhost:5001/] 網址

  • 請切換到 [命令提示字元視窗] 看到底下的內容

    因為許多檔案或者程式都已經讀取到瀏覽器快取儲存體內,因此,此時只會有一個 HTTP Request 請求產生

  • 請點選瀏覽器畫面上的 [Privacy] 連結文字

  • 此時,從 [命令提示字元視窗] 內,僅會看到有 路由路徑 /Home/Privacy 請求產生

  • 到了這裡可以理解到 ASP.NET Core 中介軟體運作的方式與設計架構

設計一個新的客製化中介軟體類別

  • 滑鼠右擊專案節點
  • 從彈出功能表中選取 [加入] > [類別]
  • 在下方 [名稱] 欄位內輸入 CustomMiddleware.cs
  • 點選右下方 [新增] 按鈕
public class CustomMiddleware
{
    private readonly RequestDelegate _next;
    public CustomMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    public async Task Invoke(HttpContext httpContext, IConfiguration configuration)
    {
        // 呼叫下一個中介軟體要執行的內容,在這裡不要寫入任何內容到 HTTP Responet 回應內
        Console.WriteLine($"呼叫第 3 個客製化中介軟體");
        await _next(httpContext);
        // 自這裡進行寫入日誌或其他工作,在這裡不要寫入任何內容到 HTTP Responet 回應內
        Console.WriteLine($"結束執行第 3 個客製化中介軟體");
        Console.WriteLine();
    }
}
public static class CustomMiddlewareExtensions
{
    public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<CustomMiddleware>();
    }
}

再加入其他客製化的中介軟體

  • 在根目錄下找到 [Startup.cs] 檔案
  • 打開 [Startup.cs] 檔案並且搜尋找到 [app.UseStaticFiles();] 敘述
  • 在該敘述下方加入底下程式碼
#region Case 2 加入額外的提供靜態檔案的選項 StaticFileOptions 
app.UseStaticFiles(new StaticFileOptions()
{
    FileProvider = new PhysicalFileProvider(
                    Path.Combine(Directory.GetCurrentDirectory(), "MyImages")),
    RequestPath = "/Images"
});
#endregion
  • 找到 [app.UseAuthorization();] 敘述
  • 在該敘述下方加入底下程式碼
#region Case 3 自行客製中介軟體 2
app.Use(async (context, next) =>
{
    Console.WriteLine($"呼叫第 2 個客製化中介軟體");
    Console.WriteLine($"路由路徑 {context.Request.Path}");
    // 限制這個 端點 僅能夠看到 "Hello World!" 文字
    if (context.Request.Path == "/Vulcan")
    {
        await context.Response.WriteAsync("Hello World!");
        return;
    }
    
    await next.Invoke();
    // 自這裡進行寫入日誌或其他工作,在這裡不要寫入任何內容到 HTTP Responet 回應內
    Console.WriteLine($"結束執行第 2 個客製化中介軟體");
});
#endregion
#region Case 4 自行客製中介軟體 3
app.UseCustomMiddleware();
#endregion

執行這個專案 - Case2 中介軟體

  • 現在額外加入了三個中介軟體到 ASP.NET Core 專案內
  • 若該專案上在執行階段,請按下 [Shift] + [F5] 來終止這個專案執行,接著請按下 [F5] 按鍵,開始執行這個專案
  • 當開啟了 [https://localhost:5001/] 網址之後,可以在 [命令提示字元視窗] 看到底下的內容
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Vulcan\GitHub\Blazor-Xamarin-Full-Stack-HOL\Examples\AC04\AC04
呼叫第 1 個客製化中介軟體
路由路徑 /
呼叫第 2 個客製化中介軟體
路由路徑 /
呼叫第 3 個客製化中介軟體
結束執行第 3 個客製化中介軟體

結束執行第 2 個客製化中介軟體
結束執行第 1 個客製化中介軟體
回應狀態碼 200
  • 從 [命令提示字元視窗] 輸出的內容可以看到,這裡專案額外加入的 [Case1] 、 [Case2] 、 [Case3] 、 [Case4] 都有被執行到,不過因為 [Case2] 是採用 ASP.NET Core 內建提供的中介軟體,因此,沒有訊息顯示在 [命令提示字元視窗] 內

  • 請在瀏覽器位址列上輸入 https://localhost:5001/Images/me.jpg 服務端點

  • 此時,可以在瀏覽器看到這個 [me.jpg] 圖片檔案

  • 而在 [命令提示字元視窗] 內會看到底下內容

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Vulcan\GitHub\Blazor-Xamarin-Full-Stack-HOL\Examples\AC04\AC04
呼叫第 1 個客製化中介軟體
路由路徑 /
呼叫第 2 個客製化中介軟體
路由路徑 /
呼叫第 3 個客製化中介軟體
結束執行第 3 個客製化中介軟體

結束執行第 2 個客製化中介軟體
結束執行第 1 個客製化中介軟體
回應狀態碼 200

呼叫第 1 個客製化中介軟體
路由路徑 /Images/me.jpg
結束執行第 1 個客製化中介軟體
回應狀態碼 200
  • 在這個 https://localhost:5001/Images/me.jpg 服務端點請求中,僅會執行了 [case1] 與 [case2] 這兩個中介軟體,因為在執行完成 [case2] 這個中介軟體之後,這個 HTTP 請求就會立即結束

執行這個專案 - Case3 中介軟體

  • 請在瀏覽器位址列上輸入 https://localhost:5001/Vulcan 服務端點
  • 此時,可以在瀏覽器看到僅僅 Hello World! 文字顯示在網頁上
  • 而在 [命令提示字元視窗] 內,僅有增加底下內容
呼叫第 1 個客製化中介軟體
路由路徑 /Vulcan
呼叫第 2 個客製化中介軟體
路由路徑 /Vulcan
結束執行第 1 個客製化中介軟體
回應狀態碼 200
  • 在這裡將會看到 [Case3] 這個中介軟體被執行到了,而 [Case4] 中介軟體沒有被執行到
  • 這是因為當 [Case3] 中介軟體執行的時候,檢查到服務端點路徑為 /Vulcan,此時,會使用這個 context.Response.WriteAsync 方法來產生 HTTP Response 回應內容,並且立即返回,也就會造成底下的中介軟體不會繼續執行。

Case4 中介軟體的設計與使用說明

對於 Case4 中介軟體的設計與使用方式,則是透過一個延伸方法來做到,這裡透過 app.UseCustomMiddleware(); 敘述來做到加入這個中介軟體到 HTTP 管道中

這裡使用加入一個新的類別 [CustomMiddleware] ,在建構函式內注入一個 [RequestDelegate] 物件,接著在這個類別內加入一個 [Invoke] 方法,滿足 HTTP 中介軟體的需求,在此方法內將會進行呼叫下一個中介軟體前後要處理的工作。

比較有趣的事情,當想要注入一個物件的時候,可以透過在 [Invoke] 方法參數來宣告,在這裡就會採用了方法注入的方式來注入想要的物件,例如,在這個範例中,將會注入一個 [IConfiguration] 物件