ASP.NET Core 菜鸟之路:从Startup.cs说起
1.前言
本文主要是以Visual Studio 2017 默認的 WebApi 模板作為基架,基于Asp .Net Core 1.0,本文面向的是初學者,如果你有 ASP.NET Core 相關實踐經驗,歡迎在評論區補充。
與早期版本的 ASP.NET 對比,最顯著的變化之一就是配置應用程序的方式, Global.asax、FilterConfig.cs 和 RouteConfig.cs 統統消失了,取而代之的是 Program.cs 和 Startup.cs。Program.cs 作為 Web 應用程序的默認入口,不做任何修改的情況下,會調用同目錄下 Startup.cs 中的 ConfigureServices 方法 和 Configure 方法。
應用啟動的流程
對于初學者來說,第一次面對 Startup.cs 往往無從下手,本文將一步步介紹作者的經驗,但是不會涉入到內部的代碼實現以及相關的原理,那并不是本文想要討論的范疇。
默認的Startup.cs
相信我,這將是你邁出構建靈活而強大的ASP.NET Core 應用程序的第一步。
2.配置參數選項
在官方文檔中提供多種方式來配置參數選項:
- 文件格式(INI,JSON和XML)
- 命令行參數
- 環境變量
- 內存中的 .NET 對象
- 用戶機密存儲
- Azure 鍵值
- 自定義提供程序
雖然提供了很多選擇,但是我們只選擇其中的JSON文件和環境變量來提供配置參數。
2.1 Json配置參數選項
參考官方文檔的示例,我們在 appsettings.json 加入如下的參數:
appsettings.json
與此同時,我們還需要一個類來映射這些配置參數:
MyOptions.cs
思考一下 subsection 應該是字典還是一個對象?如果是字典,是否可以為<string,dynamic>或者<string,object>?
好了,現在就差怎么讓他們聯系起來,只需在 ConfigureServices 方法中將他們配對:
services.Configure<MyOptions>(Configuration);最后就是解決怎么使用這些配置參數的問題了,舉個最簡單的例子,我們可以在某個控制器中把我們的所有參數打印出來:
不知道你有沒有發現 MyOptions 類中有些屬性首字母大寫,有些屬性沒有,并不是與json文件中完全一致,也就是說可以忽略大小寫的。
回到之前的匹配環節,我們可以發現 services.Configure 的方法中似乎還有更多選擇,比如我們把之前的那一行代碼替換為:
services.Configure<MyOptions>(Configuration.GetSection("wizards"));//選擇wizards節點我們可以發現返回的對象里面的屬性都為null,這是因為json中的 "wizards"的節點并不能與我們的類匹配。
那么問題來了,如果匹配的代碼如下,又會產生什么樣的結果呢?先別急著回答,我會在下一節中給出答案。
2.2環境變量
環境變量,或者說系統變量,在windows中我們可以在系統屬性中配置:
在Linux環境中也有相應的配置,但是在開發過程中,我們可以在 Visual Studio 中配置:
在這之前,我們的Json文件中已經有 "option1" 和 "option2"的參數選項,那么會產生什么樣的結果呢?
顯然我們可以看到環境變量的參數覆蓋了Json文件的參數。而引起這種變化的原因還是需要回到Startup的初始化:
從中我們可以看出環境變量的配置在讀取 Json 文件參數之后,這樣就會覆蓋已經存在的同名參數,而已經從 Json 文件被匹配的參數并不會被清空(同樣適用于前一節提出的問題)。從另一方面來說,如果你不需要環境變量,則需要去掉 "AddEnvironmentVariables() ",以免覆蓋預期參數。
回到本節的中心,我們為什么會需要環境變量呢?我個人會在Dockerfile中配置一些環境變量,比如某種服務的訪問地址、某中功能的開關等等。下面舉例說說兩個常用的環境變量:
ASPNETCORE_URLS?如果你沒有指定對應的 Url 監聽地址,可以通過該參數修改,如設置為 "http://*:80"。
ASPNETCORE_ENVIRONMENT?開發環境(Development)、預演環境(Staging)、生產環境(Production),將在工作環境一節中做詳細介紹。不同的工作環境將使得整個軟件流程變得清晰。
2.3配置參數小貼士
- 參數有多種來源,如不需要勿增來源。
- 要注意"最近原則",避免參數同名引起沖突。
- 參數的key可以忽略大小寫,所以環境變量中的 "OPTION2" 可以引起覆蓋 Json文件中的 "option2" 的效果。
- 為復雜參數選擇合適的類型很重要,比如字典還是對象的取舍。
3.依賴注入
依賴注入在 ASP.NET Core 中無處不存在,在之前打印參數的例子中同樣用到。依賴注入好處都有啥?為什么我們需要依賴注入?在?ASP .NET Core 中文文檔中依賴注入的章節?很好地解釋了:
你應該設計你的依賴注入服務來獲取它們的合作者。這意味著在你的服務中避免使用有狀態的靜態方法調用(代碼被稱為?static cling)和直接實例化依賴的類型。當選擇實例化一個類型還是通過依賴注入請求它時,它可以幫助記住這句話,?New is Glue。通過遵循?面向對象設計的 SOLID 原則,你的類將傾向于小、易于分解及易于測試。
3.1注冊服務以及簡單使用
為了方便下一節測試,我準備三個文件,簡單的接口、該接口的實現類,擁有接口成員的類:
IRepository
MemoryRepository
ProductTotalizer
接下來,我們使用 ASP.NET Core 自帶的 DI 來注冊服務:
可以看到,注冊對象有很多種方法,并且我們可以管理對象的生命周期。注冊完對象,我們就可以在我們需要的地方注入對應的對象了,還是最簡單的例子——在控制器中使用:
?
對于控制器,我們有三種方式注入對象:構造函數、控制器動作、屬性注入。然而,在一般的類中,使用自帶的 DI 只能是構造函數注入。到底是哪種方式好,見仁見智。
3.2生命周期
ASP.NET Core 服務可以被配置為以下生命周期:
- 瞬時(Transient)?在它們每次請求時都會被創建。這一生命周期適合輕量級的,無狀態的服務。
- 作用域 (Scoped)?在每次請求中只創建一次。
- 單例(Singleton)?在它們第一次被請求時創建(或者如果你在 ConfigureServices運行時指定一個實例)并且每個后續請求將使用相同的實例。
我們將通過逐步更改 IRepository 的生命周期來看看會發生什么事情。
首先是瞬時:
接著是作用域:
最后是單例:
瞬時很好理解,類似每次都會new了一個對象。而對于作用域,如果一次請求中,有兩個甚至多個非單例對象引用到同一個作用域類型時,他們將會收獲同一個實例。單例也很好理解,從頭到尾都是同一個實例。
?
控制單一變量,如果只是改變 ProductTotalizer 的生命周期而不是改變 IRepository 的生命周期的話,會發生什么情況呢?
3.3依賴注入小貼士
- 遵循 SOLID 原則,考慮一下哪些是需要依賴注入的。
- 合理考慮生命周期,假如某個類型在不同上下文中需要不同生命周期時,是否需要顯式命名區分?還是考慮結構是否合理?
- 避免靜態訪問服務。
- 避免靜態訪問?HttpContext?。
4.啟用擴展
在項目中我們往往會添加許多擴展,比如用于API文檔說明的Swagger、計劃任務的Hangfire、壓縮響應的GZIP、跨域訪問、日志擴展等等。他們的共同點就是需要先安裝相應的nuget包,然后在 ConfigureServices() 方法中配置服務,最后在 Configure() 方法中啟用。
我們以Swagger為例,首先是安裝對應的 nuget 包——?Swashbuckle。
接著是配置擴展:
最后就是啟用 Swagger 了:
我們訪問 Swagger 的地址看看效果:
5.中間件
中間件是用于組成應用程序管道來處理請求和響應的組件。管道內的每一個組件都可以選擇是否將請求交給下一個組件、并在管道中調用下一個組件之前和之后執行某些操作。請求委托被用來建立請求管道,請求委托處理每一個 HTTP 請求。
中間件處理請求
舉一個簡單的例子(更復雜的可以在中間件依賴注入對象),從 cookie 中獲取 token 并附加到請求頭中:
與啟用擴展一樣,我們同樣是需要在 Configure()方法中啟用中間件:
?
app.UseMiddleware<JWTInHeaderMiddleware>();如果我們有多個中間件呢,中間件的順序可能會影響到響應結果,但并不是總是線性相關的。例如,我們新增一個對響應狀態碼處理的中間件:
我們把它加到其他中間件的最前面: app.UseMiddleware<StatusCodeMiddleware>(); //.... app.UseMiddleware<JWTInHeaderMiddleware>();
雖然對狀態碼處理的中間件是最前面,但可以在請求的最后關頭對請求結果進行處理。當然,如果中間有某個中間件短路了(沒有傳遞到下一個中間件),就會讓我們前功盡棄。
測試多個中間件處理請求
6.過濾器
與中間相似,過濾器同樣可以對請求的前后執行特定代碼,但是過濾器可以配置為全局有效、僅對控制器有效或是僅對 Action 有效,比中間件更具有靈活性。
過濾器處理請求
另外,過濾器從類型上還能分為:授權過濾器、資源過濾器、Action過濾器、結果過濾器。很容易實現面向切面編程,降低了耦合。
這里舉一個我最喜歡的過濾器——對請求的模型進行驗證:
在控制器或 Action 使用,只需加上特性即可:
當然,模型驗證的過濾器往往具有全局性,所以我一般是加在 services.AddMvc 中:
?
services.AddMvc(config=> {config.Filters.Add(new ValidateModel());});7.工作環境
ASP.NET Core 提供了許多功能和約定來允許開發者更容易的控制在不同的環境中他們的應用程序的行為。當發布一個應用程序從開發到預演再到生產,為環境設置適當的環境變量允許對應用程序的調試,測試或生產使用進行適當的優化。
在軟件開發的生命周期中,在不同的工作環境中往往是不同的狀態。比如說,在測試或者預演環境中,啟用Swagger擴展、控制臺打印日志、允許跨域,而在生產環境中,往往處于安全、性能或者其他考慮,這些功能是需要禁止的。對于 MVC 開發者來說,在開發過程中會使用本地的JS、Css、圖片等文件,而在生產環境中這些文件往往是從CDN中獲取。
8.結語
ASP.NET Core 提供了強大而靈活的配置機制,每個點展開都像是一片新的天地。即使是經驗豐富的開發者,也不能自稱完全掌握全部機制。隨著 .NET Core 的完善和發展,Startup.cs 也將越來越復雜。越是復雜,就越是要小心,如無需要,勿增實體!
9.相關引用
- Configuration in ASP.NET Core
- ASP.NET Core 中文文檔
轉載于:https://www.cnblogs.com/chenug/p/6869109.html
總結
以上是生活随笔為你收集整理的ASP.NET Core 菜鸟之路:从Startup.cs说起的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于虚函数的应用(10个例子)
- 下一篇: JBoss/Wildfly 配置SQLs