多奇數位創意有限公司. 技術總監WILL 保哥. ASP.NET MVC 實戰經驗分享 ... 何謂「
實戰經驗」? 弄砸了4 個網站以上. 因為玩Preview, Beta, RC而重構5次以上.
ASP.NET MVC 實戰經驗分享 多奇數位創意有限公司 技術總監 WILL 保哥
何謂「實戰經驗」? 弄砸了4 個網站以上 因為玩Preview, Beta, RC而重構5次以上 寫出有 Bug 的程式 20 次以上
寫過至少 50 個Controller 寫過至少200個Action 套過至少200個View 有網站同時線上人數達3,000人 有網站每日上線人數達30,000人次
ASP.NET MVC 開發經驗一年
有什麼實戰經驗可分享? 快速上手、學習方法 協同開發、團隊分工 高效能的ASP.NET MVC網站 高執行效能 高開發效率 安全的ASP.NET MVC網站 單元測試的經驗分享 開發人員應注意的事 常見問題解答
QA Time
ASP.NET Web Form 與 ASP.NET MVC 的差異
ASP.NET Web Form 與 ASP.NET MVC 的差異 ASP.NET Web Form
ASP.NET MVC 1.0/2.0
ViewState
ASP.NET 頁面追蹤機制 (Page Trace)
ASP.NET 事件驅動模型 (Event Model)
伺服器控制項 (Server Control)
部分支援
System.Web.SiteMapProvider 類別
ASP.NET Provider Model
System.Web.Caching 命名空間
System.Web.SessionState 命名空間
System.Web.Security 命名空間
Profile, Membership, SiteMap
其他 System.Web.* 功能
支援功能
ASP.NET Web Form 與 ASP.NET MVC 的差異 相同之處 都是 IHttpHandler 的產物 共享 ASP.NET Framework 的所有功能 IL 指令集差很多 System.Web.Mvc.*: 16,902 IL instructions 雖然 ASP.NET MVC 的 WebFormView 用到許多原本 WebForm 的 東西,但是還是很多控制項不會去用他! ASP.NET MVC 的 ViewPage 繼承 System.Web.UI.Page (非常邪惡)
System.Web.UI.*: 238,179 IL instructions
快速上手、學習方法
簡化版的 ASP.NET MVC 執行生命週期 HTTP 要求
• GET /Product/Detail/1
Controller
• ProductController.Detail(1)
Model View HTTP 回應
• ProductRepository.GetProduct(1)
• ~/Views/Product/Detail.aspx
• HTML, XML, RSS, Text, CSS, File (Binary), …
簡化版的 ASP.NET MVC 執行生命週期 HTTP 要求
• POST /Product/Edit/1
Controller
• ProductController.Edit(1, FormCollection c) • TryUpdateModel(product, new string[] { “Name"})
Model View HTTP 回應
• var product = ProductRepository.GetProduct(1) • ProductRepository.UpdateProduct(1, product)
• N/A
• HTTP 重導向(Redirection)
瞭解 ASP.NET MVC 執行生命週期 Request
Controller Factory
Controller
URL Routing
Http Handler
View Factory
Route
Route Handler
View
Response
快速上手
先強調不傷身體再講求效果 先建立正確觀念 瞭解 ASP.NET MVC 執行生命週期 以 System.Web 命名空間為基礎,所有功能都還在 關注點分離 (Separate of Concerns; SoC) 要簡單、要複雜,決定權在 “你” 瞭解現階段的限制 需處理更多的 HTML, CSS, JavaScript 缺乏元件化技術與視覺化開發工具支援 理解 ASP.NET Web Form 有哪些成分不見了
關注點分離 (Separate of Concerns; SoC) 簡化複雜度 將複雜的問題拆解成數個容易解決的單元 一次僅關注於一個較容易理解與解決的部分 可維護性大幅提昇 不止可區分Model、View、Controller 還可以再切割成更多層次,讓專案更易於維護
更容易測試 僅測試需要關注的點 測試導向先行 (Test-Driven Development; TDD)
瞭解現階段的限制 需處理更多的 HTML, CSS, JavaScript 對於負責處理 View 的開發人員來說 HTML, CSS, JavaScript 是必備技能 學會使用 jQuery 能有效提升開發速度
建議讓 HTML, CSS 的責任切割出去 聘請專業的網頁設計人員專心處理 HTML, CSS 讓開發人員調整網頁版面是很沒效率
熟悉 JavaScript 是開發人員的天命 請當成一個「程式語言」來看,而非「小工具」 推薦書籍:JavaScript 大全 (第五版) JavaScript: The Definitive Guide, 5/e
缺乏元件化技術與視覺化開發工具支援
母雞帶小雞的學習模式 開發經理擔當 Pilot 研究抽象概念並與專案成員分享 找幾個小案子做實驗性開發 不斷的 Code Review 與 Code Refactoring 累積經驗
不同技術不同人研究 Design Pattern TDD LINQ to SQL , LINQ to Entities AutoMapper Moq …
協同開發、團隊分工
開發方法論 透過 MVC 分階段開發 Model Controller View Model Repository Controller View Model Repository Service Controller View IoC / DI / AutoMapper / Unit Test / Integration Test 透過關注點分離簡化複雜度 漸進式擴充各 ASP.NET MVC 擴充點 HTML Helper, AtionFilter, Model Binder, Model Validation, … 定時做 Code Refactoring 推薦書籍:重構-向範式前進 (Refactoring to Patterns)
團體合作的ASP.NET MVC開發策略 架構師專注 Model 設計資料結構、資料驗證規則、商業邏輯(Business Logic) LINQ to SQL, Repository, Service, DAL Model Validation, Model Binder, DataAnnotation 分析師專注 Controller 設計網頁與後端的互動關係 URL Routing, Controller, Actions, Action Filter 開發人員專注 View 決定前端呈現的細節、實做Action的程式碼、利用Service物件 開發人員千萬不要自己承攬 HTML, CSS 的編修工作 撰寫單元測試程式 (非整合測試)
高效能的ASP.NET MVC網站
寫出高效能的程式碼 善用 ASP.NET 快取機制與用戶端快取機制 OutputCache Cache API Client-side Cache 適當的透過 HTML Helper 產生 URL 的方式 Strong-Typed HTML Helper Template HTML Helper ( ASP.NET MVC 2.0 ) 適度的使用 CDN (Content Delivery Network) 最小化與合併 Scripts、CSS 與 Images (CSS Spirit) 部署時 用 Release 模式建置網站 移除不要的 HttpModule 關閉 web.config 中的 debug 模式 HTTP 壓縮 ( 要小心 CPU 問題 )
縮短開發時間‧提升開發速度 累積經驗,撰寫程式碼產生器 專注於解決問題,而非程式碼 透過關注點分離簡化複雜度進而減少維護時間 將問題釐清,並適度分離物件責任,以達到權責分工的目的 一個物件僅負責一件事,一個 ViewModel 只給一個 View用 撰寫可靠的單元測試程式 每個人都要能寫出可讓你們自己信任的測試程式 撰寫可靠的整合測試程式 透過專職的測試人員撰寫整合測試案例程式 確保程式的執行結果與需求一致,保證軟體品質不會退化
安全的ASP.NET MVC網站
適當的選擇 Action Selector 讓 Action 不要這麼容易曝光在網路上 有限制的允許 HTML 輸入 [ValidateInput(false)] ( 預設為true) 所有輸出的資料都要做 HTML.Encode ( ASP.NET 4.0 ) 瞭解 Model Binder 與 Model Validation 的風險 Under-Post Over-Post Non-null data type 適當的處理錯誤 [HandleError] Views\Shared\Error.aspx 原始碼檢測工具 至今沒有任何一家源碼檢測軟體能夠檢測 ASP.NET MVC 專案,因為他們連入口 點(Entry Point)都找不到
單元測試的經驗分享
何謂「單元測試」? 測試的最小單位,必須是可信任的、可重複執行的。
例如: 測試某一個類別(Class)的某一個方法(Method)
必須與「整合測試」做非常清楚的切割,兩者個概念完全
不同
DAL (Data Access Layer) 的程式不建議撰寫「單元測試」,因為 DAL 的程式會與資料庫直接產生關聯,而資料庫中可能還會有 Trigger, Stored Procedure, 表格關聯, … 等,這些東西都會打破「單元測試」 的原則,所以建議放入「整合測試」來進行。
單元測試程式不應該接觸到任何與任何 外部資源
(External Resources) 或 靜態物件 (Statics)。
例如: File I/O, 資料庫操作, 網路連線, … 等等,且由於靜態方法會牽 扯到狀態,所以不建議在單元測試中存取任何靜態物件。
只能確保程序如你預期的執行(不保證結果)
何謂「整合測試」? 針對軟體專案的一部份或全部進行測試,可以跨越不
同的類別與方法,並可直接存取的外部資源。
例如: File I/O, 資料庫操作, 網路連線, … 等等。
通常做「整合測試」都會需要先設置(Configure)測試
所需的環境,測試完畢後通常要清除測試所產生的殘 留資料,以利下次測試或避免影響其他整合測試的結 果。 確保軟體執行後的結果與需求相符合!
成功導入 TDD 的三個成功要素 信任你的測試結果 ( Trustworthiness )
你是否能信任你的測試結果? 如果你不斷的對測試結果失去信心,那麼你也不會繼續堅持撰寫單元 測試
測試程式的可維護性 ( Maintainability )
你是否能夠持續的維護你的測試程式? 如何有效的降低維護測試程式的成本?
測試程式的可讀性 ( Readability )
你的測試程式的命名是否易於理解? 當你測試失敗時是否能從測試失敗的測試方法(TestMethod)明確看出 實際失敗的原因? 當讀取測試數據的人看不懂你的測試,人們就不會執行這些測試、也 不會去維護這些測試,久而久之就會越來越惡化。
「整合測試」與「單元測試」的差異 Storage 類別 Run 方法
呼叫 Service Layer 中的 Logger 物件的 Log 方法
單元測試 確保 Storage.Run() 執行時有確實執行 Logger 物件的 Log 方法 驗證輸入的參數格式符合基本要求 整合測試 確保 Storage.Run() 執行完後,Logger物件的 Log 方法確實有 將 Log 寫入資料庫或檔案!(確認外部資源) 確保 Storage 類別與客戶的需求一致!
如何聞出「單元測試程式」的壞味道 每個 TestMethod 之間不能有任何關聯 每個 TestMethod 之間不能有任何執行順序的要求 不要使用 Setup 或 Teardown
一個 TestMethod 不要同時間驗證(Assert)兩件事以上 一個 TestMethod 中如果有兩個以上的 Assert 命令,通常 90% 的情況都是有問題的,但如果這兩個以上的 Assert 命令是為了 驗證同一件事,那就可以接受。 重點在於你所定義的 TestMethod 是不是一個完整的「邏輯測試 單位」,如果你測試的是「一件事」但有多個可以驗證的目標, 那就可以擁有多的 Assert 驗證。
單元測試中的 TestMethod 的命名規則
方法名稱會切割成三段,每段以底線 ( _ ) 分隔 第 1 段:被測試的 Method 名稱 第 2 段:測試的情境(Scenario) 第 3 段:預期測試的結果
不以「單字」作為分段的依據,而是以這三段的分別來分
隔 例如:
第 1 段:我們要測試 ChangePassword 方法 第 2 段:情境是透過 HTTP GET 取得頁面時 第 3 段:預期會回傳一個 ViewResult 這時該測試方法的命名就可以為:
ChangePassword_Get_ReturnViewResult
對測試專案的組織方法 假設你原本專案名稱為:ProjectX 單元測試專案可命名為:ProjectX.Tests 整合測試專案可命名為:ProjectX.IntegratedTests 剛開始導入單元測試建議一個專案對應一個單元測試
專案,先不要多個專案共用一個單元測試專案
單元測試到底要寫多少才夠? 寫到你相信你的測試結果 ( Trustworthiness ) ! 先弄清楚「單元測試」與「整合測試」的差異! 錯誤的觀念會讓你不信任你的測試程式。 錯誤的假設會導致無止境的測試迴圈!(因為不信任而導致無止 盡的撰寫測試程式) 正向測試 v.s 負面測試 先測試合理的輸入值,確認功能與架構符合需求 再測試無理的輸入值,確認超出範圍的輸入值可否被正確處理
開發人員應注意的事 我的最佳實務 MY BEST PRACTICES
通則 關注點分離 (Separate of Concerns; SoC) 不要讓一個物件處理兩件事! 慣例優於組態(Convention over Configuration) 命名規則、目錄結構 多學習一些設計樣式(Design Pattern)的知識 GRASP (Object Oriented Design) Single responsibility principle Factory method pattern ……
Model 要肥、Controller 要輕、View 要夠笨
開發人員應注意的事 (Model) DomainModel != ViewModel 有時後並非所有 DomainModel 都適合傳給 View 顯示 有時後畫面要顯示的欄位不見得在 DomainModel 會有
例如:註冊會員畫面有兩個 Password 欄位
善加利用開發工具可有效加速開發流程 可考慮導入 LINQ to SQL 或其他 ORM 技術 實做 Repository Pattern 負責存取資料(類似Data Access Layer; DAL) 實做 Service Pattern 負責商業邏輯(類似Business Logic Layer; BLL)
開發人員應注意的事 (View) 千萬不要用義大利麵式的寫法(早期ASP 的開發模式) 千萬不要用 Code Behind 的寫法 如果可以的話,盡量少用 HTML Helper ,能用
HTML 表達的就用 HTML 撰寫
便於閱讀 效率
如果View的邏輯過於複雜,還是可以考慮撰寫 HTML
Helper
Response.Write(“義大利麵”)
注意不可為 Null 的實質型別參數 public ActionResult Index(int page) public ActionResult Index(int? page)
開發人員應注意的事 (Controller) 謹慎的使用 BaseController,過多的程式會影響效能 可將 ActionFilter 視為「共享」資料的管道 善用 ViewModel ,少量的資料可使用 ViewData 注意實值型別參數不能為null的特性(可轉成 Nullable 型別) 善用 PRG Pattern
[HttpPost] 、 [HttpGet] 屬性 Post Redirect Get
利用 RouteDebugger 測試 URL Routing 不要在 Controller 指派 ViewName,養成習慣
(Convension)才能有效提升開發效率 一個 Action 僅搭配一個 ViewModel
ASP.NET MVC常見問題解答
ASP.NET MVC常見問題解答 ASP.NET MVC 的 View 是不是回歸早期 ASP 的寫法? ASP.NET MVC 與ASP.NET Web Form的差別? 以 System.Web 命名空間為基礎 除了 Page Framework 之外,所有功能都能用 ASP.NET Web Form 與 ASP.NET MVC 比較表 ASP.NET Web Form 與 ASP.NET MVC 可否並存?
ASP.NET MVC 可否以 WebSite 專案形式執行? ASP.NET MVC 常見問題解答 Part 1
聯絡資訊 THE WILL WILL WEB 記載著 WILL 在網路世界的學習心得與技術分享
http://blog.miniasp.com/ ★ ★ ★ WILL 保哥的噗浪 ★ ★ ★
http://www.plurk.com/willh/invite MY TWITTER
http://twitter.com/Will_Huang