前幾天在博問中看到一個問題——Response.End()后,是否停止執行?MVC與WebForm不一致。看到LZ的描述后,雖然奇怪於為何用Response.End()而不用return方式去控制流程,但基於自己以往的認識,還是回答了說需要return。
因為以往的開發過程中,雖然沒有用過Response.End()的方式像LZ所說地那樣“方便地從多層調用中退出”,但是始終是認為Response.End()是不能終止其后代碼執行的,思維路線大概是:Response.End()只是結束了HTTP返回流的寫入,但是代碼依然沒有return啊,例如Page_Load中使用了Response.End(),但是這個方法並沒有被跳出/終止。
之后LZ編輯了問題,繼續提到了問題沒有解決,又附帶了偽代碼希望大家幫忙改進書寫方式。直到此時,由於自己的思維慣性,我依然我沒有去寫DEMO去驗證對比webform和mvc下的Response.End(),簡單地用主動throw new Exception的方式寫出了MVC下“好看一點”的代碼。
之后在回復中,LZ再次重復了Response.End()確實在webform和mvc中存在差異,我抱着試一試地心態測了一個療程。真的有點吃驚,Reponse.End()在webfrom和ASP.NET MVC下的表現確實是不同的!
ASP.NET MVC代碼:
public ActionResult Index() { Method0(); Method1(); Method2(); Response.Write("All methods success."); return View("I don't think so."); } private void Method0() { Debug.WriteLine("Method 0 process..."); bool flag = true; if (!flag) { Response.Write("Method 0 failure."); Response.End(); } } private void Method1() { Debug.WriteLine("Method 1 process..."); bool flag = false; if (!flag) { Response.Write("Method 1 failure."); Response.End(); } } private void Method2() { Debug.WriteLine("Method 2 process..."); bool flag = false; if (!flag) { Response.Write("Method 2 failure."); Response.End(); } }
web頁面顯示:
調試信息輸出:
Response.End()后的代碼繼續執行,這與之前的認識是沒有出入的,接下來看webform。
Webform代碼:
protected void Page_Load(object sender, EventArgs e) { Method0(); Method1(); Method2(); Response.Write("All methods success."); } private void Method0() { Debug.WriteLine("Method 0 process..."); bool flag = true; if (!flag) { HttpContext.Current.Response.Write("Method 0 failure."); System.Web.HttpContext.Current.Response.End(); } } private void Method1() { Debug.WriteLine("Method 1 process..."); bool flag = false; if (!flag) { HttpContext.Current.Response.Write("Method 1 failure."); System.Web.HttpContext.Current.Response.End(); } } private void Method2() { Debug.WriteLine("Method 2 process..."); bool flag = true; if (!flag) { HttpContext.Current.Response.Write("Method 2 failure."); System.Web.HttpContext.Current.Response.End(); } }
web頁面輸出:
調試信息:
web頁面的輸出一致,調試窗口那里可是大不一樣。webform並未接着執行Response.End()后的代碼,因為拋出了一個ThreadAbortException異常。這時候,我首先想到的是ASP.NET MVC下的Response對象類型是否和ASP.NET不同,導致他們的處理方式不同。
后來發現雖然ASP.NET MVC中的Response類型是HttpResponseBase,但是顯式地去調用System.Web.Context.Current.Response.End()結果依舊。通過Reflector查看ASP.NET MVC下HttpResponseBase的實現類HttpResponseWrapper,End方法的實現如圖,this_httpResponse是HttpResponse的私有變量。
查到這兒思路一度中斷,只好回頭去對比調試信息中的表現,從ThreadAbortException這個異常入手,發現在ASP.NET MVC中先調用Response.End(),再調用Thread.CurrentThread.Abort()可以達到webform下調用Response.End()的效果。當然其他異常也能模擬,但是此時發現了一個小問題,就是拋出普通異常的時候和拋出ThreadAbortException異常略有不同。
普通異常的彈出窗口:
調試信息輸出:
ThreadAbortException異常沒有彈出那個窗口,調試信息中也多了一條信息。
是由於ThreadAbortException是SystemException(系統異常)被特殊對待了嗎?
這只是一個衍生出來的疑問,繼續剛才的問題,用ThreadAbortException和ASP.NET MVC作為關鍵字去google搜索,在Will保哥的博客中得到了解答!
經過保哥的指點,通過Reflector去查看源碼,證實了是_timeoutState的作用。
HttpResponse.End中代碼:
IsInCancellablePeriod屬性:
問題得到了解決!~但是我還有一個小疑問,也就是從Reflector中看到End方法的源碼,IsInCancellablePeriod是bool類型,但是卻判斷是否等於null。這怎么也是不合適的吧,是Reflector的解析錯誤還是其他原因導致的呢?