MVC系列——一个异常消息传递引发的思考
前言:最近在某個項目里面遇到一個有點糾結的小問題,經過半天時間的思索和嘗試,問題得到解決。在此記錄一下解決的過程,以及解決問題的過程中對.net里面MVC異常處理的思考。都是些老生常談的問題,不多說,直接上“主菜”。
本文原創地址:http://www.cnblogs.com/landeanfen/p/8135844.html
一、問題重現
項目是一個傳統.net framework的MVC項目,為了簡便,項目里面定義了一個自定義異常類用于向客戶端傳遞錯誤消息,客戶端接收到異常的消息時在瀏覽器里面彈出提示。先來看看這個自定義異常類CustormerException的定義
public class CustomerException : System.Exception{public CustomerException(){}public CustomerException(string message) : base(message){}public CustomerException(string message, params object[] args) : base(string.Format(message, args)){}}為了模擬重現問題,我盡量將代碼簡化再簡化。
[BaseException]public class DefaultController : Controller{// GET: Defaultpublic ActionResult Index(){return View();}public JsonResult Login(string userName, string password){if (userName == "admin" && password == "admin"){return Json(true, JsonRequestBehavior.AllowGet);}else{throw new CustomerException("用戶名或者密碼錯誤");}}}public class BaseExceptionAttribute : HandleErrorAttribute{public override void OnException(ExceptionContext filterContext){if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception is CustomerException){filterContext.ExceptionHandled = true;filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;var result = new ContentResult() { Content = filterContext.Exception.Message, ContentType = MediaTypeNames.Text.Plain };filterContext.Result = result;}else{//記錄日志 }base.OnException(filterContext);}}代碼不復雜,就是一個通用的異常過濾器,用于記錄日志和傳遞消息到客戶端。
然后我們看看客戶端的測試代碼:
@{ViewBag.Title = "Index"; }用戶名:<input type="text" id="username"/> 密碼 :<input type="text" id="password"/> <button id="btnAjaxError" type="button">登陸</button> @section Script {<script type="text/javascript">$(function () {$("#btnAjaxError").click(function () {$.ajax({url:"/Default/Login",data: { userName: $('#username').val(), password: $('#password').val() },type: 'post'}).done(function (data) {console.log("successful:"+data);}).fail(function (a, b, c) {debugger;console.log("fail:"+a.responseText);});});}); </script> }本地調試、運行得到正常結果
?發布到IIS,本地訪問仍然正常。可是當我們遠程訪問的時候問題出現了。
?
所有的遠程訪問機器上面都出現了系統默認的錯誤消息,而不是我們返回的業務異常消息。
二、初次嘗試
對于這種本地能看到詳細異常,而遠程看不到詳細異常的問題,相信有一定經驗的朋友肯定想到了一個配置,那就是Web.config里面的CustomErrors節點,我們配置下默認開啟自定義異常不就行了嗎。嘿嘿!就是這么簡單!博主當初也是這么樂呵呵的去嘗試的。我們在Web.config的System.web節點下面加入這個節點
<customErrors mode="On"></customErrors>可是很遺憾,問題依舊!后來想是不是自己對于On、Off、RemoteOnly的理解有誤?于是乎三個項逐個嘗試,結果均已失敗告終!
于是乎開始有點郁悶了,這種問題原來怎么沒遇到過了,代碼“貌似”沒什么大問題啊,如果有問題,本地應該也不能得到才對啊。于是乎分析,這可能不是我們代碼的問題,而是IIS給我做了一層統一的異常處理,我們只需要將這層統一的異常處理去掉就行了啊。道理是這么個道理,可是如何去實現呢。于是乎把IIS的各個功能都試了個遍,最后的谷歌的一篇帖子里面找到了一些幫助。解決方案如下。
三、解決方案
1、“不是代碼的問題”的解決方案
上文說到這個問題或許不是代碼的問題,而是IIS配置的問題。于是乎真的讓博主找到了解決方案。解決步驟如下:
?
?
?
原來,IIS默認是不讓遠程用戶查看異常的詳細錯誤的,如果是遠程用戶,IIS會默認給你返回一個各種狀態碼對應的默認消息,我們自定義的消息將會被此覆蓋。如果改成選中第二項,就表示不管是本地用戶還是遠程用戶均可以看到詳細異常。
這樣配置之后不用更改任何代碼,不用理會是否配置了CustomErrors節點,遠程用戶均可以正常獲取到程序返回的異常消息:
2、“是代碼的問題”的解決方案
有了上面的解決方案,為何還會有“是代碼的問題”的解決方案呢?這才是本文想要表達的中心思想。既然我們通過配置IIS的錯誤頁可以解決這個問題,那么我們為什么不能在程序的范疇內去解決呢?博主是一個有點喜歡刨根問題的人,不斷分析代碼后發現,既然系統的默認錯誤消息可以覆蓋我們的自定義異常消息,那么反過來,我們自定義的異常消息為什么就不能覆蓋系統默認的異常消息呢?于是乎發現在重寫父類的OnException方法的時候,上面的代碼我們是先執行的我們自定義的異常消息,然后再調用?base.OnException(filterContext);?去執行系統默認的異常消息處理的,那么我們將這個順序倒置一下,反過來是不是可行呢?于是代碼就變成了這樣:
public class BaseExceptionAttribute : HandleErrorAttribute{public override void OnException(ExceptionContext filterContext){base.OnException(filterContext);if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception is CustomerException){filterContext.ExceptionHandled = true;filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest;var result = new ContentResult() { Content = filterContext.Exception.Message, ContentType = MediaTypeNames.Text.Plain };filterContext.Result = result;}else{//記錄日志 }}}?我們將上面通過配置IIS錯誤頁的解決方案還原,改成默認的配置。去掉CustomErrors節點,重新發布之后,問題完美解決:
問題能解決,說明博主上面的推想或許是正確的,自定義異常和默認異常是存在一個先后順序的,我們如果要覆蓋系統的異常,需要將我們自定義異常的代碼放在后面執行。這個論斷是通過上述解決問題的思路推理得來的,并不一定正確,有興趣的可以反編譯下dll看下是否真是這樣!
很有趣的一點就是,這樣改了代碼之后,我們如果在web.config里面加入customErrors節點,并且將mode設置為Off,遠程訪問的時候得到的異常消息又變成了“錯誤的請求”。其實這不難理解,當你禁用自定義錯誤信息,那么系統肯定會給你返回默認的異常信息了。
四、總結
由上述的兩種解決方案可以看出這里其實有三道防線:
第一道防線是最外層的防線,就是IIS的錯誤頁配置,如果這層配置選擇的是詳細錯誤,那么不管你其他的配置是什么樣,都會返回用戶自定義的錯誤信息;
第二道防線是中間的那層,就是web.config里面的CustomErrors節點,如果第一道防線是默認配置,這層防線才會生效;
第三道防線才是代碼的范疇,這個受限于CustomErrors節點的配置。
本文原創出處:http://www.cnblogs.com/landeanfen/
歡迎各位轉載,但是未經作者本人同意,轉載文章之后必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責任的權利
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的MVC系列——一个异常消息传递引发的思考的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 摩拜活跃用户跌200万、使用次数跌640
- 下一篇: ubuntu下配置eclipse3.3