前言:最近在某個項目裡面遇到一個有點糾結的小問題,經過半天時間的思索和嘗試,問題得到解決。在此記錄一下解決的過程,以及解決問題的過程中對.net裡面MVC異常處理的思考。都是些老生常談的問題,不多說,直接上“主菜”。
本文原創位址:http://www.cnblogs.com/landeanfen/p/8135844.html
項目是一個傳統.net framework的MVC項目,為了簡便,項目裡面定義了一個自定義異常類用于向用戶端傳遞錯誤消息,用戶端接收到異常的消息時在浏覽器裡面彈出提示。先來看看這個自定義異常類CustormerException的定義
為了模拟重制問題,我盡量将代碼簡化再簡化。
代碼不複雜,就是一個通用的異常過濾器,用于記錄日志和傳遞消息到用戶端。
然後我們看看用戶端的測試代碼:
本地調試、運作得到正常結果

釋出到IIS,本地通路仍然正常。可是當我們遠端通路的時候問題出現了。
所有的遠端通路機器上面都出現了系統預設的錯誤消息,而不是我們傳回的業務異常消息。
對于這種本地能看到詳細異常,而遠端看不到詳細異常的問題,相信有一定經驗的朋友肯定想到了一個配置,那就是Web.config裡面的CustomErrors節點,我們配置下預設開啟自定義異常不就行了嗎。嘿嘿!就是這麼簡單!部落客當初也是這麼樂呵呵的去嘗試的。我們在Web.config的System.web節點下面加入這個節點
可是很遺憾,問題依舊!後來想是不是自己對于On、Off、RemoteOnly的了解有誤?于是乎三個項逐個嘗試,結果均已失敗告終!
于是乎開始有點郁悶了,這種問題原來怎麼沒遇到過了,代碼“貌似”沒什麼大問題啊,如果有問題,本地應該也不能得到才對啊。于是乎分析,這可能不是我們代碼的問題,而是IIS給我做了一層統一的異常處理,我們隻需要将這層統一的異常處理去掉就行了啊。道理是這麼個道理,可是如何去實作呢。于是乎把IIS的各個功能都試了個遍,最後的谷歌的一篇文章裡面找到了一些幫助。解決方案如下。
上文說到這個問題或許不是代碼的問題,而是IIS配置的問題。于是乎真的讓部落客找到了解決方案。解決步驟如下:
原來,IIS預設是不讓遠端使用者檢視異常的詳細錯誤的,如果是遠端使用者,IIS會預設給你傳回一個各種狀态碼對應的預設消息,我們自定義的消息将會被此覆寫。如果改成選中第二項,就表示不管是本地使用者還是遠端使用者均可以看到詳細異常。
這樣配置之後不用更改任何代碼,不用理會是否配置了CustomErrors節點,遠端使用者均可以正常擷取到程式傳回的異常消息:
有了上面的解決方案,為何還會有“是代碼的問題”的解決方案呢?這才是本文想要表達的中心思想。既然我們通過配置IIS的錯誤頁可以解決這個問題,那麼我們為什麼不能在程式的範疇内去解決呢?部落客是一個有點喜歡刨根問題的人,不斷分析代碼後發現,既然系統的預設錯誤消息可以覆寫我們的自定義異常消息,那麼反過來,我們自定義的異常消息為什麼就不能覆寫系統預設的異常消息呢?于是乎發現在重寫父類的OnException方法的時候,上面的代碼我們是先執行的我們自定義的異常消息,然後再調用 base.OnException(filterContext); 去執行系統預設的異常消息處理的,那麼我們将這個順序倒置一下,反過來是不是可行呢?于是代碼就變成了這樣:
我們将上面通過配置IIS錯誤頁的解決方案還原,改成預設的配置。去掉CustomErrors節點,重新釋出之後,問題完美解決:
問題能解決,說明部落客上面的推想或許是正确的,自定義異常和預設異常是存在一個先後順序的,我們如果要覆寫系統的異常,需要将我們自定義異常的代碼放在後面執行。這個論斷是通過上述解決問題的思路推理得來的,并不一定正确,有興趣的可以反編譯下dll看下是否真是這樣!
很有趣的一點就是,這樣改了代碼之後,我們如果在web.config裡面加入customErrors節點,并且将mode設定為Off,遠端通路的時候得到的異常消息又變成了“錯誤的請求”。其實這不難了解,當你禁用自定義錯誤資訊,那麼系統肯定會給你傳回預設的異常資訊了。
由上述的兩種解決方案可以看出這裡其實有三道防線:
第一道防線是最外層的防線,就是IIS的錯誤頁配置,如果這層配置選擇的是詳細錯誤,那麼不管你其他的配置是什麼樣,都會傳回使用者自定義的錯誤資訊;
第二道防線是中間的那層,就是web.config裡面的CustomErrors節點,如果第一道防線是預設配置,這層防線才會生效;
第三道防線才是代碼的範疇,這個受限于CustomErrors節點的配置。
本文原創出處:http://www.cnblogs.com/landeanfen/
歡迎各位轉載,但是未經作者本人同意,轉載文章之後必須在文章頁面明顯位置給出作者和原文連接配接,否則保留追究法律責任的權利