2021年12月8日 星期三

如何使用 Syncfusion 元件庫 在 Blazor 專案上

如何使用 Syncfusion 元件庫 在 Blazor 專案上

由於工作上的需要以看到最新的網頁開發框架 Blazor ,便決定開始使用 Blazor Server Side 來進行網頁專案的開發,會選擇 Server Side 方式而不是採用 WASM 的方式,當初決定的理由相當的簡單,那就是要簡化整個網站開發的流程,透過 Blazor Server Side 開發架構,可以享受到許多的好處,例如可以線上直接設定中斷點來進行除錯、不需要設計 Web API 就可以存取後端的資料庫等等。

一旦決定了使用 Blazor 開發框架來進行專案開發,接下來要解決的問題就是如何設計各種ui介面,在網路上存在的許多免費的 Blazor UI 套件,每種使用方式以提供的功能都不盡相同,根據自身的需求來判斷,似乎沒有一套免費的ui套件可以滿足專案上的需要,因此就直接轉向了付費的 Blazor UI 套件來進行評估,當然,市面上也存在的許多付費的 Blazor 元件產品,在這裡選擇了 Syncfusion 這家的 Blazor 元件來進行開發。

那麼要如何使用 Syncfusion 元件來進行 Blazor 專案開發,就是這篇文章所要探討的內容,在這裡,將會說明如何在 Blazor 專案內顯示一個日期選舉萬年曆這樣的 UI 功能。

這個說明專案的原始碼位於 bzSidebar

建立 Blazor Server-Side 的專案

  • 打開 Visual Studio 2019

  • 點選右下方的 [建立新的專案] 按鈕

  • [建立新專案] 對話窗將會顯示在螢幕上

  • 從[建立新專案] 對話窗的中間區域,找到 [Blazor 應用程式] 這個專案樣板選項,並且選擇這個項目

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

  • 現在 [設定新的專案] 對話窗將會出現

  • 請在這個對話窗內,輸入適當的 [專案名稱] 、 [位置] 、 [解決方案名稱]

    在這裡請輸入 [專案名稱] 為 bzSyncfusion

  • 完成後,請點選 [建立] 按鈕

  • 當出現 [建立新的 Blazor 應用程式] 對話窗的時候

  • 請選擇最新版本的 .NET Core 與 [Blazor 伺服器應用程式]

  • 完成後,請點選 [建立] 按鈕

稍微等會一段時間,Blazor 專案將會建立起來

進行 Syncfusion 元件的安裝

  • 滑鼠右擊 Blazor 專案的 [相依性] 節點
  • 選擇 [管理 NuGet 套件]
  • 切換到 [瀏覽] 標籤頁次
  • 搜尋 Syncfusion.Blazor 這個元件名稱
  • 選擇搜尋到的 [Syncfusion.Blazor] 元件,並且安裝起來

進行 Syncfusion 元件的設定

  • 打開專案根目錄下的 [Startup.cs] 這個檔案
  • 找到 [ConfigureServices] 這個方法
  • 在這個方法的最後面,加入底下程式碼,已完成 Blazor 元件會用到的服務註冊
#region Syncfusion 元件的服務註冊
services.AddSyncfusionBlazor();
#endregion
  • 在同一個檔案內,找到 [Configure] 這個方法
  • 在這個方法的最前面,加入底下程式碼,宣告合法授權的金鑰 (License Key)
#region 宣告所使用 Syncfusion for Blazor 元件的使用授權碼
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR LICENSE KEY");
#endregion
  • 打開 [Pages] 資料夾內的 [_Host.cshtml] 檔案

  • 在 <head> 標籤內,加入需要的 CSS 宣告,如底下內容

    若沒有加入底下的宣告,將無法正常看到 Syncfusion 的元件樣貌

<link href="_content/Syncfusion.Blazor/styles/bootstrap4.css" rel="stylesheet" />

使用 SfCalendar 元件

  • 在專案的 [Pages] 資料夾
  • 打開 [Index.razor] 檔案
  • 使用底下程式碼替換到 [Index.razor] 檔案內容
@page "/"
@using Syncfusion.Blazor
@using Syncfusion.Blazor.Calendars

<h1>Hello, world!</h1>

Welcome to your new app.

<SfCalendar TValue="DateTime" Value="workDate"></SfCalendar>

@code
{
    DateTime workDate = DateTime.Now;
}

從這裡看到使用 Syncfusion 元件相當的容易,只需要加入該元件所使用的命名空間在 Razor 元元件前面,接著就加入該元件的HTML標籤,例如在這裡使用的是

現在可以直接執行這支程式,就會看到如下圖的執行結果。

 




 




2021年9月16日 星期四

Blazor實戰故事經驗分享 1 - 風起雲湧 如何從無到有建立Blazor團隊與採用全端開發方式設計出給上市企業使用的Web系統

Blazor實戰故事經驗分享 1 - 風起雲湧 如何從無到有建立Blazor團隊與採用全端開發方式設計出給上市企業使用的Web系統

摘要

(Blazor 實戰故事經驗分享 Part 2 : 風雲再現 探究 Blazor 可以快速開發出來內部細節)

這是一個系列文章,說明作者與好友 Steve 如何從無到有打造出一個使用 Blazor 開發框架技術的3人團隊,沒有前端與後端之分,沒有使用 JavaScript,便可以順利在三個月內設計出 Web 應用程式與 Windows Forms 的平版應用程式,當然,最重要的是將產品交付給客戶,完成此一任務的經驗分享;故事的結局當然是十分完美,要不然也不會有這一系列文章與讀者也不需要繼續來觀看這些文章了。

在這個專案內,共使用到底下的技術與內容

起源

作者於今年四月離開了待了快要八年的公司,不要問我問甚麼要離開、是哪裡不爽快、有甚麼恩怨,沒有,沒有,沒有,甚麼都沒有,要對號入座,請自己挑好位置,最好準備好觀落陰的工具,也許你會有更好的八卦主題。

而在今年因為寫了一本 Blazor 快速體驗 電子書,趁著離職後的閒暇時間,也把這本書的各個實作過程,錄製成為 Youtube 影片 Blazor 快速體驗 之 線上操作教學影片,有興趣的人,可以訂閱本頻道

作者從 2018 年底開始接觸 Blazor,一邊研究 Blazor 實驗性版本,也一邊寫出許多關於 Blazor 應用的文章 約 28 篇,而在研究過程中,讓我想起想要能夠充分掌握 Blazor 的設計精神與發揮這個開發框架的價值,據以傳統的 Desktop 設計經驗,也就是類似 Client / Server 經驗的人,更能夠進入狀況;這讓我突然想起快要 30 年的好友,我們當初一起研究與使用 Delphi 這套開發工具 (而我本身是從事於 Delphi 的推廣與教育訓練課程的規劃與授課) ,那就是人稱 陳少 的 Steve。

找個時間到高軟與陳少碰面,談起了 Blazor & Delphi 的經驗,瞬間找回當初在研究 Delphi 的時光,而我們兩人都是從來無緣接觸過 Web 開發的人,而我本身對於 JavaScript 這個程式語言,從他一發表來,都沒有任何好感 (我依稀記得,當初 Borland 也有推出關於 JavaScript 的開發工具,無奈無緣),所以,當我一提起 Blaror,陳少瞬間了解 Blazor 的價值,也看出對於許多開發者而言,透過 Blazor 這套框架,是可以獨立一個人開發出 Web 應用程式,根本不需要做甚麼前後端分離的做法。

對於為什麼要做到前後端分離,這需要各位看官自行去了解當初的背景,以及這樣的作法是要解決甚麼樣的問題,並且透過這樣的做法會帶來甚麼樣的好處,以及要付出甚麼樣的代價。想想,當初在使用 Client / Server 開發環境下,每個 Delphi 程式設計師不都是甚麼前後端工作都一手包辦,而且這些專案也都有開發出來,那麼,為什麼現在不能夠在 Web 開發環境下,一樣可以做到這樣的事情呢? (你知道問題在哪裡嗎?沒錯,那就是 JavaScript,我從來沒有喜歡過這個語言與其相關環境,更不用說他在不同瀏覽器下造成的許多不一致現象與許多詭異問題)。

好了,既然英雄有志一同,就請他根據 先看過 Blazor 快速體驗 電子書 與 Blazor 快速體驗 之 線上操作教學影片 ,之後,順理成章也就在五月份針對如何使用 Blazor 開發的過程與相關應用,針對陳少做了一場一天的訓練,不過,請注意到,陳少其實對於 C# 並不是十分孰悉,不過,對於整場的訓練下來之後,陳少發現到原來他也可以輕鬆自在的開發出 Web 應用程式。

所謂的機緣就是這麼奇妙,突然間 陳少 被邀請到另外一家公司內,負責整個專案研發部門的管理工作;然而,實際的狀況是這個部門就僅有他一個人,他要負責把原先公司使用 PHP & MySQL 開發出來的多套產品,重新設計出來(為什麼不去改善原有的 PHP 系統,這是因為原有系統已經值得打掉重練的五顆星評價),更為險峻的是原有系統的許多開發文件、系統架構都極為混亂與不見了;看樣子是無法看著原先的程式碼,逐一轉換成為另外一套系統,順而求其次,只好看著原有使用手冊畫面,猜測與理解原先系統背後的運作方式。另外,這些產品都有 Windows Forms 的用戶端與手機 App 也需要與新系統能夠串接起來,當然,二話不說, Windows Forms 與原生 Android / iOS App 也是需要打掉重練。

現在,問題來了,要選擇甚麼樣的開發環境來進行這樣的專案開發,要找到多少人來重新打造這樣的系統出來,當初接收到的指示是期望能夠在 2020 年底,打造出新一代的產品出來。

既然身為好友,現在又沒有工作,我就想說要不要我來幫忙一起設計與打造出這樣的團隊與開發出這樣的產品,對我而言,這是一個自我挑戰,也是驗證我對於 Blazor 這個開發框架的眼光是否準確,不過,我僅能夠每周投入個1~2天的時間,幫忙一起進行這樣的挑戰,,其餘的時間,我想要把之前沒有開發完成的 .NET C# 相關教育訓練教材完成,例如,多執行緒方面方面的課程(現在這個課程已經整理到快要 400 頁的程度了,這其中還包含了來自於微軟官方技術支援的方想);因此,這個 Blazor實戰故事經驗分享 應該要就此開始囉...

人員招募

由於陳少與我都是高雄人,他所待的公司自然是在高雄,不過,對於要找到可以具備擁有開發 Blazor 的程式設計師,在高雄真的滿大的挑戰;經過分析過後,我決定使用之前設計 Xamarin.Forms 教育訓練課程的技巧,在最短的時間內,設計出讓具備 .NET / C# 能力的程式設計師,就算沒有甚麼 Web 開發經驗的人,也可以進行 Blazor 專案開發。

不要以為我瘋了,或者我話唬爛,因為,最後證明這樣做法,可以讓程式設計師都可以進行 Blazor 專案開發,並且開發出交付給客戶產品。

另外,有鑑於整個系統的 UI/UX 的設計考量

  • UI

    讓畫面更好看

  • UX

    讓系統更好用

根據上面的準則,首先要解決 UI 上的問題,雖然,在 Blazor 還在實驗性階段,就已經有許多免費的 UI 套件,當然,到現在為止,原先的 UI 套件更加的豐富與完整,而且還有更多的 UI 套件也陸續推出;為了避免選擇或者整合不同 UI 套件的困擾,以及這些 UI 套件在升級過程中,產生了中斷變更 Break-Change 的問題,陳少決定使用 Syncfusion 這家的 Blazor 付費套件。

其實,選擇哪家付費 Blazor UI 套件並不是十分難選擇,先分析這個專案會有哪些的 UI ,這家的原件是否可以滿足需求即可,最重要的是,雖然 Blazor 正式版本在今年才推出,幾乎市面上聽得過名字的有名元件公司,都有提供 Blazor 的原件,這些元件當然都是原生的,若沒有問題,使用上完全使用 C# 來設定與呼叫,幾乎不會用到任何一行 JavaScript 程式碼。

從這裡可以看的出來,各家付費元件廠商早在 Blazor 在實驗性產品的時候,就已經推出相對應的 Blazor 加值元件,從此可以看的出來,各家元件廠商都相當看好 Blazor 這個開發平台,要不然,賠錢的生意沒人做,殺頭的生意有人做,各家元件廠商是不會傻到投入一個沒有前途的開發平台元件上。

有了讓應用程式是否好看的決定性要素,接下來要解決是否好用的因素,這裡將會設計一個通用型的 CRUD SS 與新增、取得、更新、刪除、搜尋、排序等應用的程式寫法,這部分也在決定要使用 Syncfusion元件之後,於五月底完成相關範例程式碼的開發工作。

在五月中旬,招募了第一個程式設計師,本身大約有業界 3 年開發經驗,之前是在上海工作,因為疫情無法回去上海,因此,決定到 陳少 這裡來上班;這位程式設計師的經歷大部分是在 Windows Forms 上,對於 Web 專案開發的經驗並沒有很多;雖然人已經招募進來了,因為新的辦公室還在裝潢中,因此,大約等到六月中旬,他才開始進駐新辦公室,準備進行相關 Blazor 開發訓練。

另外一位工程是在六月的時候招募進來,剛好是前一位程式設計師的同校、同屆、同科系、但沒有同班,算是有機緣可以一起共事;這樣程式設計之前是在新竹友達駐廠,使用 ASP.NET MVC 負責開發系統。

這兩位工程是共同的特色那就是

  • 都有 .NET C# 的開發經驗
  • 程式設計年資都差不多
  • 不過,開發領域不相同
  • 沒有任何 Blazor 的開發經驗
  • 沒有任何 ASP.NET Core 的開發經驗
  • 對於相關近代的設計模式或者技術,例如 相依性注入、非同步程式設計等技能,也沒有經驗
  • 對於資料庫存取、觀念與 Entity Framework Core 也沒有經驗
  • JavaScript 應該功力不深

至於第三位程式設計師大約是在八月份招募進來,大約有超過八年以上的經驗,不過,最近10年的工作,大多是在前端開發,使用 Vue 開發框架,當然,JavaScript 有一定的經驗,不過,對於 .NET / C# 則是很早之前工作上用到的。

好了,就在 7月11日,陳少接收到公司總經理的訊息,詢問是否可以提前先完成已經成交的某上市公司案子,這個案子預計要能夠在 10月27日交付,並且所開發出來的產品,要能夠經過客戶的確認與驗收,更悲慘的是,這個產品案子要能夠存 SQL Server、Oracle、SQLite 三個資料庫系統,而且也需要包含一個 Windows Forms 的前台應用程式,透過 SQLite 資料庫做到離線與同步處理的能力。

結論

看到這裡,若你是陳少,你會怎麼解決這樣的問題

  • 要能夠使用新的工具與平台,打造出公司新一代的產品
  • 大約只有三個月的時間可以來支配
  • 相關要開發的程式設計師具備的經驗與經歷都不盡相同
  • 要開發該產品的領域知識 Domain-How 也還沒摸清楚
  • 這個新一代的產品,究竟要使用甚麼樣的開發技術
  • 所選定的技術,以現有人力,是否足夠呢
  • 對於所選定的技術架構,對於日後開發產品上又會有何影響

相信大家對 陳少 一定捏一把冷汗,他究竟會如何解決這些問題?反觀,若是你承接了這樣的工作,你又會怎麼處理。

最重要的是,這樣的運作模式決定之後,對於日後相關新系統的開發與人力支配,又會該如何成長,如何做到最佳經濟成本,獲得絕佳的效益。

而且,未來將會要做到超全端 Hyper Full Stack 的開發模式,讓一個工程師可以獨力完成 Web App的前後台系統、跨平台手機 App,這也是要納入思考的。

繼續觀賞第二部分

相關文章

Blazor實戰故事經驗分享 1 - 風起雲湧 如何從無到有建立Blazor團隊與採用全端開發方式設計出給上市企業使用的Web系統

Blazor實戰故事經驗分享 2 - 風雲再現 探究 Blazor 可以快速開發出來內部細節


 

2021年7月30日 星期五

Blazor Server 必會開發技能 - C# 與 JavaScript 互相呼叫

Blazor Server 必會開發技能 - C# 與 JavaScript 互相呼叫


Blazor Server 必會開發技能

Blazor Server 建立一個新的頁面

Blazor Server 元件生命週期事件 Component Life Cycle

Blazor Server C# 程式碼設計方法

Blazor Server 單向資料綁定 One Way Data Binding 與 重新轉譯 Binding

Blazor Server Hello 互動頁面與事件設計 Two Way Data Binding

Blazor Server 元件間的參數傳遞與回應事件 Component Parameter EventCallback

Blazor Server C# 與 JavaScript 互相呼叫 IJSRuntime

Blazor Server 表單和驗證 Form Validation 


當在使用 Blazor 進行網站應用系統開發的時候,原則上,不再需要使用到 JavaScript 程式語言,不過,有些時候還是要需要能夠讓 C# 來呼叫 JavaScript 程式碼或者在 JavaScript 程式語言中來呼叫 .NET C# 的靜態或者執行個體公開方法,例如,想要把 Google Map 這樣的應用需求加入到 Blazor 專案內,就有可能需要進行這樣的設計,當然,也可以使用已經把這些需求打包成為 Blazor 套件來使用,在這篇文章將會來練習如何設計這樣的程式碼。

這裡說明的範例專案原始碼位於 BS07

建立 Blazor Server-Side 的專案

  • 打開 Visual Studio 2019

  • 點選右下方的 [建立新的專案] 按鈕

  • [建立新專案] 對話窗將會顯示在螢幕上

  • 從[建立新專案] 對話窗的中間區域,找到 [Blazor 應用程式] 這個專案樣板選項,並且選擇這個項目

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

  • 現在 [設定新的專案] 對話窗將會出現

  • 請在這個對話窗內,輸入適當的 [專案名稱] 、 [位置] 、 [解決方案名稱]

    在這裡請輸入 [專案名稱] 為 BS07

  • 完成後,請點選 [建立] 按鈕

  • 當出現 [建立新的 Blazor 應用程式] 對話窗的時候

  • 請選擇最新版本的 .NET Core 與 [Blazor 伺服器應用程式]

  • 完成後,請點選 [建立] 按鈕

    稍微等會一段時間,Blazor 專案將會建立起來

加入 JavaScript 程式碼

  • 打開 [Pages] 資料夾內的 [_Host.cshtml] 檔案
  • 在 <head> 區段加入底下的 JavaScript 程式碼
@*客製化的 JavaScript*@
<script>
    window.helloWorld = {
        askYourName: function (title, hint) {
            return prompt(title, hint);
        },
        sayYourAgeToCsharp: function (dotNetHelper) {
            age = prompt('請輸入你的年紀', '所輸入數值將會傳遞給 C# 方法')
            dotNetHelper.invokeMethodAsync('GetStringFromJavaScript', age);
        },
    };
</script>

修改 Index.razor 元件

  • 打開 [Pages] 資料夾內的 [Index.razor] 檔案
  • 請使用底下程式碼替換到這個檔案內容
@page "/"
@inject IJSRuntime JSRuntime
<h1>Hello, C# 與 JavaScript 互相呼叫!</h1>

<button class="btn btn-primary" @onclick="AskYourName">你是誰? (C# -> JS)</button>
<div class="display-4 text-success">@Name</div>
<button class="btn btn-primary my-4" @onclick="AskYourAge">你幾歲? (C# -> JS -> C#)</button>
<div class="display-4 text-danger">@Message</div>

@code {
    public string Name { get; set; }
    public string Message { get; set; }
    private DotNetObjectReference<Index> objRef;

    async Task AskYourName()
    {
        Name = await JSRuntime.InvokeAsync<string>(
            "helloWorld.askYourName", "你是誰", "請輸入你的姓名");
    }

    async Task AskYourAge()
    {
        DotNetObjectReference<Index> objRef = DotNetObjectReference.Create(this);
        await JSRuntime.InvokeVoidAsync("helloWorld.sayYourAgeToCsharp", objRef);
    }

    [JSInvokable]
    public void GetStringFromJavaScript(string message)
    {
        Message = message;
    }
}

首先來看看如何在 C# 程式碼內來呼叫 JavaScript 內的方法,要能夠做到這樣的程式設計,首先要先使用 @inject 指示詞 Directive 來注入 [IJSRuntime] 這個物件。

在 [你是誰? (C# -> JS)] 按鈕所綁定的委派事件中,使用了這樣的敘述 Name = await JSRuntime.InvokeAsync<string>("helloWorld.askYourName", "你是誰", "請輸入你的姓名"); 來呼叫 JavaScript 方法,在 [JSRuntime.InvokeAsync] 方法的第一個參數將會是要呼叫的 [JavaScript] 方法名稱,而後的引數將會是要傳遞到 JavaScript 方法參數。

從這個 [window.helloWorld] 方法可以看出,該 [JavaScript] 方法使用了 [Prompt] 方法,要求使用者輸入一個文字字串,一旦使用者輸入完成後,就會回傳到 .NET C# 方法內了,因此,透過 [Name] 這個變數,就可以取得使用者輸入的文字內容

在這個 [你幾歲? (C# -> JS -> C#)] 按鈕中,將會呈現先使用 C# 呼叫一個 [JavaScript] 方法,接著,在 [JavaScript] 程式碼內,將會透過了 dotNetHelper.invokeMethodAsync('GetStringFromJavaScript', age); 方法來呼叫 .NET C# 內的 [GetStringFromJavaScript]。

首先,在 C# 按鈕方法內將會使用 DotNetObjectReference<Index> objRef = DotNetObjectReference.Create(this); 敘述來建立一個 [DotNetObjectReference] 物件,這裡的泛型型別參數將會表示將要採用執行個體的方法來呼叫,而不是使用靜態的方法 (要在 JavaScript 內呼叫 .NET 的靜態方法,需要使用 [DotNet.invokeMethodAsync] 方式);這裡產生的物件將會傳入到 JavaScrip 內,接著,在 [JavaScript] 內就可以使用這個物件來呼叫 .NET C# 物件的方法了。

最後,為了要能夠讓 [JavaScript] 呼叫 .NET C# 的方法,該方法需要使用 [JSInvokable] 屬性標住在方法前面,代表這個方法可以被 .NET C# 呼叫之用。

執行這個專案

  • 按下 [F5] 按鍵,開始執行這個 Blazor 專案

  • 一旦啟動完成,就會自動開以瀏覽器

  • 請點選 [你是誰? (C# -> JS)] 按鈕

  • 現在在網頁上將會出現一個對話窗,請輸入任何文字並且點選 [確定] 按鈕

  • 請點選 [你幾歲? (C# -> JS -> C#)] 按鈕

  • 現在在網頁上將會出現一個對話窗,請輸入年紀並且點選 [確定] 按鈕

  • 此時,可以從網頁上看到剛剛在使用 [JavaScript] 程式碼所取得的文字

 










2021年7月26日 星期一

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 MVC 專案

  • 開啟 Visual Studio 2019

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

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

    1. 設定程式語言為 [C#]

    2. 設定專案範本為 [Web]

    3. 選擇專案範本項目清單,點選 [ASP.NET Core Web API] 這個專案範本項目

      用於建立 ASP.NET Core 應用程式的專案範本,附有 RESTful HTTP 服務的控制器範例。此範本也可用於 ASP.NET Core MVC 的檢視及控制器。

    4. 點選右下方的 [下一步] 按鈕

  • 在 [設定新的專案] 對話窗出現後

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

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

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

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

建立服務類別與介面

  • 滑鼠右擊專案節點
  • 從彈出功能表來選取 [加入] > [類別]
  • 當出現 [新增項目 - AC07] 對話窗
  • 在下方 [名稱] 欄位輸入 [UnhandledExceptionMiddleware.cs]
  • 在右下方點選 [新增] 按鈕
  • 使用底下的程式碼替換掉這個檔案原先內容
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

namespace AC07
{
    public class UnhandledExceptionMiddleware
    {
        private readonly ILogger logger;
        private readonly RequestDelegate next;

        public UnhandledExceptionMiddleware(ILogger<UnhandledExceptionMiddleware> logger, RequestDelegate next)
        {
            this.logger = logger;
            this.next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await next(context);
                if(IsSuccessStatusCode(context.Response))
                {
                    logger.LogDebug($"HTTP 回應成功 {context.Response.StatusCode}");
                }
                else
                {
                    logger.LogWarning($"HTTP 回應不是成功代碼 {context.Response.StatusCode}");
                }
            }
            catch (Exception exception)
            {
                logger.LogError(exception,
                    $"Request {context.Request?.Method}: {context.Request?.Path.Value} failed");
            }
        }
        public bool IsSuccessStatusCode(HttpResponse response)
        {
            return response.StatusCode >= 200 && response.StatusCode <= 299;
        }
    }
}

設計新的 API 控制器

  • 滑鼠右擊 [Controllers] 資料夾
  • 從彈出功能表清單中點選 [加入] > [控制器]
  • 當 [新增 Scaffold 項目] 對話窗出現之後
  • 依序點選 [已安裝] > [通用] > [API] > [API 控制器 - 空白]
  • 點選右下角的 [加入] 按鈕
  • 現在將會看到 [新增項目 - AC07] 對話窗
  • 在下方的 [名稱] 欄位中輸入 WebLogController
  • 點選右下方 [新增] 按鈕
  • 使用底下的程式碼替換這個檔案內容
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace AC07.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class WebLogController : ControllerBase
    {
        public WebLogController(ILogger<WebLogController> logger)
        {
            Logger = logger;
        }

        public ILogger<WebLogController> Logger { get; }

        [HttpGet]
        public async Task<string> Get()
        {
            StringBuilder result = new StringBuilder();

            var client = new HttpClient();

            try
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                
                Logger.LogDebug($"開始讀取 Google 網頁資料");
                string message = await client.GetStringAsync("http://www.google.com");
                Logger.LogInformation($"完成讀取 Google 網頁資料");
                stopwatch.Stop();
                result.Append($"讀取 google 資料共花費 {stopwatch.ElapsedMilliseconds} ms" + Environment.NewLine);
                
                Logger.LogInformation($"開始讀取 m1cr050ft 網頁資料");
                message = await client.GetStringAsync("http://www.m1cr050ft.com");
                Logger.LogInformation($"完成讀取 m1cr050ft 網頁資料");
                result.Append($"讀取 micr0s0ft 資料共花費 {stopwatch.ElapsedMilliseconds} ms" + Environment.NewLine);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, $"使用 HttpClient 發生了問題");
            }

            return result.ToString();
        }
    }
}

執行這個專案

  • 請按下 [F5] 按鍵,開始執行這個專案

  • 瀏覽器的畫面如下

  • 請在瀏覽器上輸入 https://localhost:5001/api/WebLog 網址

  • 將會看到底下的畫面

  • 切換到執行這個專案的 [命令提示字元視窗] 下

  • 將會看到底下的內容

info: AC07.Controllers.WebLogController[0]
      完成讀取 Google 網頁資料
info: AC07.Controllers.WebLogController[0]
      開始讀取 m1cr050ft 網頁資料
fail: AC07.Controllers.WebLogController[0]
      使用 HttpClient 發生了問題
      System.Net.Http.HttpRequestException: Response status code does not indicate success: 403 (Forbidden).
         at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
         at System.Net.Http.HttpClient.GetStringAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
         at AC07.Controllers.WebLogController.Get() in D:\Vulcan\GitHub\Blazor-Xamarin-Full-Stack-HOL\Examples\AC07\AC07\Controllers\WebLogController.cs:line 41
warn: AC07.UnhandledExceptionMiddleware[0]
      HTTP 回應不是成功代碼 404
  • 在這個 public async Task<string> Get() 方法內,看到原本對於有呼叫 Logger.LogDebug($"開始讀取 Google 網頁資料"); 方法,要把這段文字內容寫入到日誌提供者內,可是卻沒有看到;而其他有使用到 [Logger] 物件所產出的日誌輸出,都可以在 [命令提示字元視窗] 下看到。

對於呼叫 await client.GetStringAsync("http://www.m1cr050ft.com"); 方法之後,因為該網址不存在,所以,會拋出例外異常,這裡也會被 try {...} catch{...} 敘述捕捉起來,並且透過 Logger.LogError(ex, $"使用 HttpClient 發生了問題"); 敘述將這個例外異常訊息寫入到日誌提供者內。

修正那些日誌資訊可以看得到

現在要來解決為什麼執行 Logger.LogDebug($"開始讀取 Google 網頁資料"); 敘述,但是在 [命令提示字元視窗] 內沒有出現;要理解這個問題,要先來看看 [appsettings.json]這個檔案的內。

  • 底下為 [appsettings.json] 檔案內的內容
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
  • 在第一個 [Logging] 屬性是用來設定日誌輸出設定
  • 從 [LogLevel] > [Default] 的屬性值看到,這個日誌提供者僅會針對 [Information] 以上等級的日誌才會寫入輸出
  • 若此時變更 [LogLevel] > [Default] 屬性值為 [Trace]
  • 按下 [Ctrl] + [F5] 按鈕,停止這個專案執行
  • 請按下 [F5] 按鍵,開始執行這個專案
  • 請在瀏覽器上輸入 https://localhost:5001/api/WebLog 網址
  • 切換到執行這個專案的 [命令提示字元視窗] 下
  • 將會看到底下的內容
info: AC07.Controllers.WebLogController[0]
      完成讀取 Google 網頁資料
info: AC07.Controllers.WebLogController[0]
      開始讀取 m1cr050ft 網頁資料
fail: AC07.Controllers.WebLogController[0]
      使用 HttpClient 發生了問題
      System.Net.Http.HttpRequestException: Response status code does not indicate success: 403 (Forbidden).
         at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
         at System.Net.Http.HttpClient.GetStringAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
         at AC07.Controllers.WebLogController.Get() in D:\Vulcan\GitHub\Blazor-Xamarin-Full-Stack-HOL\Examples\AC07\AC07\Controllers\WebLogController.cs:line 41
warn: AC07.UnhandledExceptionMiddleware[0]
      HTTP 回應不是成功代碼 404
  • 不過,還是沒有看到 Logger.LogDebug($"開始讀取 Google 網頁資料"); 這個日誌的輸出
  • 這是因為對於 [ASP.NET Core 設定] 功能
  • 對於在開發除錯模式下
  • 當要讀取 設定 屬性值,會先讀取 [appsettings.json] 內的設定屬性值
  • 底下為 [appsettings.Development.json] 檔案內的內容
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}
  • 接著,會來讀取 [appsettings.Development.json] 內的設定屬性值,並且會優先使用這個檔案內的屬性值

因此,雖然在 [appsettings.json] 內變更 [LogLevel] > [Default] 屬性值為 [Trace],不過在 [appsettings.Development.json] 內的 [LogLevel] > [Default] 屬性值為 [Information],所以,還是排除掉了 [Debug] 這個等級的日誌訊息。

  • 現在修正 [appsettings.Development.json] 內的 [LogLevel] > [Default] 屬性值為 [Trace]
  • 按下 [Ctrl] + [F5] 按鈕,停止這個專案執行
  • 請按下 [F5] 按鍵,開始執行這個專案
  • 請在瀏覽器上輸入 https://localhost:5001/api/WebLog 網址
  • 切換到執行這個專案的 [命令提示字元視窗] 下
  • 將會看到底下的內容
dbug: AC07.UnhandledExceptionMiddleware[0]
      HTTP 回應成功 200
dbug: AC07.UnhandledExceptionMiddleware[0]
      HTTP 回應成功 200
dbug: AC07.Controllers.WebLogController[0]
      開始讀取 Google 網頁資料
info: AC07.Controllers.WebLogController[0]
      完成讀取 Google 網頁資料
info: AC07.Controllers.WebLogController[0]
      開始讀取 m1cr050ft 網頁資料
fail: AC07.Controllers.WebLogController[0]
      使用 HttpClient 發生了問題
      System.Net.Http.HttpRequestException: Response status code does not indicate success: 403 (Forbidden).
         at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
         at System.Net.Http.HttpClient.GetStringAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
         at AC07.Controllers.WebLogController.Get() in D:\Vulcan\GitHub\Blazor-Xamarin-Full-Stack-HOL\Examples\AC07\AC07\Controllers\WebLogController.cs:line 41
dbug: AC07.UnhandledExceptionMiddleware[0]
      HTTP 回應成功 200
warn: AC07.UnhandledExceptionMiddleware[0] 

      HTTP 回應不是成功代碼 404