用ASP.NET Core 2.0 建立規範的 REST API -- 預備知識 (2) + 準備專案
如果您對ASP.NET Core很瞭解的話,可以不看本文, 本文基本都是官方文件的內容。
ASP.NET Core 預備知識
專案配置
假設在專案的根目錄有這樣一個json檔案, 在ASP.NET Core專案裡我們可以使用IConfigurationRoot來使用該json檔案作為配置檔案, 而IConfigurationRoot是使用ConfigurationBuilder來建立的:
可以看到ConfigurationBuilder載入了firstConfig.json檔案, 使用的是AddJsonFile這個擴充套件方法. 呼叫builder的Build方法會得到一個IConfigurationRoot
其中json檔案裡的結構資料都最為鍵值對被扁平化到IConfiguration裡了, 我們可以通過它的key找到對應的值:
像childkey1這種帶層次結構的值可以使用冒號 : 作為層次分隔符.
配置檔案總會包含這種多層結構的, 更好的辦法是把類似的配置進行分組獲取, 可以使用IConfiguration的GetSection()方法來獲取區域性的配置:
當有多個配置檔案的時候, 配置資料的載入和它們在程式中指定的順序是一樣的, 如果多個檔案都有同一個鍵的話, 那麼最後載入的值將會覆蓋先前載入的值.
下面是另一個配置檔案:
在firstConfig後加載secondConfig:
最後key1的值是後加載的secondConfig裡面的值.
當然了, 如果firstConfig裡面有而secondConfig卻沒有的鍵, 它的值肯定來自firstConfig.
配置提供商
配置資料可以來自多種資料來源, 它們可能是不同格式的.
ASP.NET Core 預設支援從下列方式獲得配置:
- 檔案格式(INI, JSON, XML)
- 命令列引數
- 環境變數
- 記憶體中的.NET物件
- 未加密的Secret管理儲存
- 加密的使用者儲存, 例如Azure祕鑰庫
- 自定義的提供商
這些東西還是看官方文件吧, 本文使用JSON格式的就夠用了.
強型別的配置
ASP.NET Core允許把配置資料對映到一個物件類上面.
針對上面的firstConfig.json檔案, 我們建立以下這個類:
然後呼叫IConfiguration的Bind擴充套件方法來把鍵值對集合對值對映到這個強型別對POCO例項裡:
在標準的ASP.NET Core 2.0的專案模版裡, 載入配置檔案的步驟被封裝了, 預設或載入appSettings.json 以及 appSettings.{環境}.json.
我記得是封裝在這裡了:
我把firstConfig.json改名為appSettings.json.
然後在Startup裡面可以獲得IConfiguration:
從列印結果可以看到, 載入的不只是appSettings裡面的內容, 還有系統環境變數的值.
這種情況下, 使用IServiceCollection的Configure擴充套件方法可以把配置對映到指定的類上面:
同時這也允許在程式的任何地方注入IOptions<FirstConfig>了:
這個Configure方法不僅僅可以對映ConfigurationRoot, 還可以對映配置的一部分:
配置變化
在專案執行的時候, 專案的配置資訊可能會發生變化.
當採用的是基於檔案的配置時, 如果配置資料有變化了, 我們應該讓配置模型重新載入, 這就需要把AddJsonFile裡面的配置屬性 ReloadOnChange 設定為 true:
這時, 無論在哪各地方使用了IConfigurationRoot和IConfiguration, 它們都會反映出最新的值, 但是IOptions<T>卻不行. 即使檔案變化了並且配置模型也通過檔案提供商進行了更新, IOptions<T>的例項仍然包含的是原始值.
為了讓配置資料可以在這種強型別對映的類上體現, 就需要使用IOptionsSnapshot<T>:
IOptionsSnapshot<T> 的開銷很小, 可以放心使用
日誌
ASP.NET Core 提供了6個內建的日誌提供商。
需要使用日誌的話,只需注入一個ILogger物件即可,不過該物件首先要在DI容器中註冊。
這個ILogger介面主要是提供了Log方法:
記錄Log的時候使用Log方法即可:
不過可以看到,該方法引數很多,用起來還是略顯麻煩的。
幸運的是,針對Log還有幾個擴充套件方法,他們就簡單了很多:
- LogCritical,用來記錄嚴重的事情
- LogDebug,記錄除錯資訊
- LogError,記錄異常
- LogInformation,記錄資訊性的事情
- LogTrace,記錄追蹤資訊
- LogWarning,記錄警告資訊
在專案中配置和使用Log,只需在Program.cs裡呼叫IWebHostBuilder的ConfigureLogging擴充套件方法即可:
本例中,我們把log配置成在控制檯輸出。
如果只是輸出到控制檯,其實我們就多此一舉了,因為CreateDefaultBuilder這個方法裡已經做了一些Log的配置,看一下反編譯的原始碼:
可以看到logging的一些配置資料是從整體配置的Logging部分取出來的,然後配置了使用輸出到控制檯和Debug視窗的提供商。
記錄Log的時候,通常情況下使用那幾個擴充套件方法就足夠了:
請注意,這裡我注入的是ILogger<T>型別的logger,其中T可以用來表示日誌的分類,它可以是任何型別,但通常是記錄日誌時所在的類。
執行專案後,可以看到我記錄的日誌:
同樣也可以在一個類裡面把記錄的日誌分為不同的分類,這時候你可以使用ILoggerFactory,這樣就可以隨時建立logger了,並把它繫結到特定的區域:
不知道您有沒有發現上面這幾個例子中日誌輸出的時候都有個數字 [0], 它是事件的識別符號。因為上面的例子中我們沒有指定事件的ID,所以就取預設值0。使用事件ID還是可以幫助我們區分和關聯記錄的日誌的。
每次寫日誌的時候, 都需要通過不同的方式指明LogLevel, LogLevel表明的是嚴重性.
下面是ASP.NET Core裡面定義的LogLevel(它是個列舉), 按嚴重性從低到高排序的:
Trace = 0, 它可以包含敏感拘束, 預設在生產環境中它是被禁用掉的.
Debug = 1, 也是在除錯使用, 應該在生產環境中禁用, 但是遇到問題需要除錯可以臨時啟用.
Information = 2, 用來追蹤應用程式的總體流程.
Warning = 3, 通常用於記錄非正常或意外的事件, 也可以包括不會導致應用程式停止的錯誤和其他事件, 例如驗證錯誤等.
Error = 4, 用於記錄無法處理的錯誤和異常, 這些資訊意味著當前的活動或操作發生了錯誤, 但不是應用程式級別的錯誤.
Critical = 5, 用於記錄需要立即處理的事件, 例如資料丟失或磁碟空間不足.
None = 6, 如果你不想輸出日誌, 你可以把程式的最低日誌級別設定為None, 此外還可以用來過濾日誌.
記錄的日誌資訊是可以帶引數的, 使用訊息模板(也就是訊息主題和引數分開), 格式如下:
同樣也支援字串插值:
第二種方式程式碼的可讀性更強一些, 而且它們輸出的結果沒有什麼區別:
但是對於日誌系統來說, 這兩種方式是不一樣的. 通過訊息模板的方式(訊息和引數分開的方式), 日誌提供商可以實現語義日誌或叫做結構化日誌, 它們可以把引數單獨的出入到日誌系統裡面進行單獨儲存, 不僅僅是格式化的日誌資訊.
此外, 用過載的方法, 記錄日誌時也可以包含異常物件.
日誌分組
我們可以使用相同的日誌資訊來表示一組操作, 這需要使用scope, scope繼承了IDisposable介面, 通過ILogger.BeginScope<TState>可以得到scope:
使用scope, 還有一點需要注意, 需要在日誌提供商上把IncludeScopes屬性設定為true:
您可以發現, 日誌被輸出了兩遍, 這是因為WebHost.CreateDefaultBuilder方法裡面已經配置使用了AddConsole()方法, 我再配置一遍的話就相當於又添加了一個輸出到控制檯的日誌提供商.
所以, 我可以不採用這個構建模式建立IWebHost, 改為直接new一個:
這樣就正確了. 可以看到日誌資訊的第一行內容是一樣的, 第二行是各自的日誌資訊.
日誌的過濾
我們可以為整個程式設定日誌記錄的最低級別, 也可以為某個日誌提供商和分類指定特定的過濾器.
設定全域性最低記錄日誌的級別使用SetMinimumLevel()擴充套件方法:
如果想完全不輸出日誌的話, 可以把最低記錄的級別設為LogLevel.None.
我們還可以為不同場景設定不同的最低記錄級別:
然後分別建立這兩個分類的logger, 並記錄:
檢視輸出結果, 已經按配置進行了過濾:
這裡可以使用完整的類名作為分類名:
然後使用ILogger<T>即可:
針對上面這個例子, 我們還可以使用配置檔案:
相應的, 程式碼也需要改一下:
輸出的效果是一樣的.
日誌提供商
ASP.NET Core 內建了6個日誌提供商:
- Console, 使用logging.AddConsole()來啟用.
- Debug, 使用logging.AddDebug()來啟用. 它使用的是System.Diagnostics.Debug的Debug.WriteLine()方法, 由於Debug類的所有成員都是被[Conditional("DEBUG")]修飾過了, 所以無法被構建到Release Build裡, 也就是生產環境是無法輸出的, 除非你把Debug Build作為部署到生產環境