[ASP.NET] 現在,哪些功能不該用?有哪些替代方法?

這篇文章是源自 ASP.NET Web Blog 的一篇文章:What not to do in ASP.NET, and what to do instead,我覺得寫得不錯,而且對目前使用 ASP.NET (尤其是 Web Form) 開發的程式設計師來說特別重要,因此我將這篇文章翻譯重點部份撰成本文。

這篇文章是源自 ASP.NET Web Blog 的一篇文章:What not to do in ASP.NET, and what to do instead,我覺得寫得不錯,而且對目前使用 ASP.NET (尤其是 Web Form) 開發的程式設計師來說特別重要,因此我將這篇文章翻譯重點部份撰成本文。

ASP.NET 到今天已經發展到 4.5 版,4.5.1 也即將發行,在各次版本的演進與更替中,往往會推出不少的新功能,或是原功能的強化,不過這些都是推出當時的時空因素所設計,HTML/CSS/JavaScript 的演進以及 RESTful API 的觀念日漸成熟,也讓 ASP.NET有很多的功能慢慢得變成不適用,因此 ASP.NET 的開發團隊就在網站上編寫文章來告訴大家哪些功能不要再使用,以及其替代功能為何。

1. 標準功能

1.1 Control Adapter (建議停用,以 HTML/CSS Media Queries 取代)

首先是 ASP.NET 2.0 推出的 Control Adapter 功能,它是為了要處理不同設備間的 HTML 繪製需求所設計,但是今天 HTML 在跨設備的功能已經被 CSS,尤其是在 Responsive Design 中,HTML5 可以配合 Media Queries 來決定如何繪製 HTML,因此 Control Adapter 已失去它原本的需求,所以建議不要再使用 Control Adapter,而是以 HTML/CSS 與 Media Queries 來替代。

1.2 控制項中的樣式屬性 (建議停用,以 CssClass 配合 CSS 類別取代)

在 ASP.NET 的控制項中都擁有一個 style 的屬性,用來給開發人員設定控制項的 C視覺樣式設定,但是目前的主流是以 CSS 來控制,因此建議不要再使用 style 來操作視覺樣式,而是應該使用 CssClass 來設定 CSS 的類別,由 CSS 類別去操控視覺的樣式。

1.3 頁面與控制項的回呼功能 (建議停用,以正規的 AJAX 作法替代)

ASP.NET 2.0 提出的 Page Callback/Control Callback 是為了早期 AJAX 技術不夠純熟時,為了要做到部份內容更新時所實作出來的暫行作法,現在因為 AJAX 技術已十分成熟,因此應該要改用正規的 AJAX 作法來替代 Page/Control Callback。

正規的 AJAX 作法包含 jQuery, ASP.NET AJAX UpdatePanel, MVC, SignalR, Web API 等 (我個人則建議最好別用 UpdatePanel)。

1.4 瀏覽器能力偵測 (建議停用靜態偵測,改用動態偵測替代)

早期的瀏覽器能力偵測是寫在 ASP.NET Server-side 裡面,且無法動態變更,屬於靜態的能力偵測,但是現在有 Modernizr 這類偵測瀏覽器是否支援特定功能的 JavaScript 程式庫出現,它的彈性會遠高於 ASP.NET 本身的靜態偵測能力,因此建議使用 Modernizr 或其他相似功能的動態偵測,來替代 ASP.NET 本身的靜態偵測。

2. 安全性

2.1 要求驗證 (建議作法為驗證使用者輸入,並將輸出做編碼)

這是較一般且基本的安全性要求,在 Web 的環境中,最好不要信任使用者的任何輸入資訊,都一定要經驗證,並且必要時要加以編碼 (例如輸入的是 HTML 的內容) 與過濾掉一些可能的危險內容 (使用 AntiXSS),以避免安全的漏洞。

要求驗證只是其中的一個必要工作,在後端也還要做很多工作來加強安全性,例如一定要用參數方式查詢與操作資料庫,以避免 SQL Injection 的問題。

2.2 無 Cookie 的表單驗證與工作狀態 (建議:不要使用無 Cookie)

無 Cookie 模式的表單驗證會修改 URL,以加入工作階段的代碼,這會讓惡意使用者獲得更多的資訊以試圖入侵,因此最好不要使用無 cookie 的作法,並且在傳輪此種 cookie 時要使用 SSL 來保護。

2.3 EnableViewStateMac (建議作法:不要將它設為 false)

這個值和 EnableViewState 不同,即便不想使用 ViewState 也不該將它設為 false,因為這會讓網站暴露在 XSS 的風險之中。

如果在 Web.config 中設定了 EnableViewStateMac 是 false,那麼就要在頁面的設定中明確設定 EnableViewStateMac=true以啟用它。

2.4 中級信任 (建議作法:不要使用中級或其他信任層級成為安全性的邊界設定)

部份信任無法確實的保護應用程式,而應該要使用完全信任,並且利用應用程式資源池 (application pool) 來隔離不信任的應用程式,但我個人認為,最好的作法就是每支 Web 應用程式都要使用不同的 application pool,除非有極特殊的需求。

2.5 <appSettings> (建議作法:不要利用 <appSettings> 停用安全性設定)

有部份 ASP.NET 的功能會使用到 <appSettings> 來儲存設定,這些設定不應該隨意停用,否則會讓 ASP.NET 本身的安全保護機制無法發揮效用。

2.6 UrlPathEncode() (建議:使用 UrlEncode() 來替代)

UrlPathEncode() 是為了要提供特殊瀏覽器相信問題的路徑編碼功能,但它並不會真旳編碼 URL,且不會保護應用程式不受 XSS 的攻擊,所以請不要再使用它,並利用 UrlEncode() 來替代。

3. 可靠性與效能

3.1 PreSendRequestHeaders/PreSendRequestContext (建議:不要在受管理的模組中使用這些事件)

PreSendRequestHeaders/PreSendRequestContext 是給原生的 IIS 模組使用,如果在受管理的模組中使用的話會導致非同步要求的問題。

3.2 Web Form 內的非同步頁面事件 (建議作法:在 Web Form 中,不要使用 async void 的方法於網頁生命週期內,而是使用 Page.RegisterAsyncTask 來註冊非同步的工作)

如果在網頁生命週期的事件常式中使用 async 的話,會讓 ASP.NET Runtime 無法得知非同步工作完成與否,因此要先使用 Page.RegisterAsyncTask() 讓 ASP.NET Runtime 知道有非同步的工作,並且採取相應的措施。

這個功能在 ASP.NET 4.5 以上版本才會有。

3.3 觸發並遺忘的工作 (建議作法,在使用 ASP.NET 處理要求時,避免使用觸發並遺忘的工作程序)

這一點和前面原因的很類似,在 ASP.NET 網頁生命週期中,作業都是同步的,所以如果在程式中使用像 ThreadPool.QueueUserWorkItem() 來登記觸發並遺忘 (Fire-and-Forget, 就是射後不理啦) 的工作時,ASP.NET Runtime 無法確認工作的結果並在結束時將程式資源釋放,連帶的 ThreadPool 也會被釋放。

如果真的需要實作這類需求,請將它移出 ASP.NET,改用 Windows Service 或是外部程式來做,在 Windows Azure 上更可以用 Worker Role 來執行此類工作,並使用 Queue 來進行通知。

3.4 要求資料的本體 (建議:不要在處理器執行事件前使用 Reuqest.Form 或是 Request.InputStream 來讀取要求資料)

這是因為 ASP.NET 核心也會使用 Request.Form/Request.InputStream 來取得資料,如果應用程式早一步 (於 ASP.NET 核心讀取資料前) 使用了 Request.Form/Request.InputStream 讀取資料的話,會讓後續的流程無法讀取資料。

若真的有此需求,請使用 Request.GetBufferlessInputStream() 或 Request.GetBufferedInputStream() 來讀取,但最好是使用 Request.GetBufferedInputStream(),因為它不會清空 Request.Form/Request.InputStream。

3.5 EnableViewState/ViewStateMode (建議:使用 ViewStateMode 替代 EnableViewState 來對 ViewState 做更精確的控制)

如果應用程式使用 ASP.NET 4.0 以上的版本,請改用 ViewStateMode 對指定的控制項做 ViewState 的控制,而不要使用 EnableViewState,因為 EnableViewState 會關閉整個網頁的 ViewState,而 ViewStateMode 允許針對個別的控制項做設定,可兼顧安全與效能。

3.6 SqlMembershipProvider (建議:改用 Universal Provider)

SqlMembershipProvider 本身使用的是早期的 SQL 指令並只支援 SQL Server 資料庫,而 Universal Provider 的會員支援可擴及到多種資料庫,且由 Entity Framework 直接支援,彈性會大於 SqlMembershipProvider 許多。

3.7 需要長時間執行 ( > 110 秒) 的要求 (建議:改用 Websockets/SignalR 替代)

由於 ASP.NET 本身的限制,並不建議使用它來做長時間執行要求的工作,最好可以改用 Websockets 或是使用 SignalR 來替代它,因為這兩種服務就是針對長時間執行的工作設計的,會在執行時有更好的效率。

 

Reference:

http://www.asp.net/aspnet/overview/aspnet-45/what-not-to-do-in-aspnet,-and-what-to-do-instead