ASP.NET MVC 錯誤處理
Handling Exceptions
這篇整理得很好:http://iamlevi.net/asp-net-mvc-4-ways-handle-exceptions/
常見的5種方法:
- Writing try..catch
- Override OnException method inside the Controller
- Using the HandleError attribute
- Extending the HandleError attribute
- Handle all errors inside Global.asax
作者結論:There is no good or bad way to handle exceptions inside ASP.NET MVC 4. Each implementation has some good and bad sides.
這裡面我最想跳過的方法是try..catch,就像作者說的,缺點是到處都是try..catch,寫起來就煩阿~
第3點最簡單,但卻無法紀錄Error Log. (所以才有第4點阿~)
以上都有2種範例:轉到Error Page(ActionResult),或者回傳Json資料告訴前端(Ajax呼叫之類的...)發生錯誤了。
在範例中,作者沒有寫到Error Log作法,如Nlog, Log4NET, Enterprise Library Logging Application Block 之類,
若大家有興趣,<ASP.NET MVC 4 網站開發美學> 這本書第八章有介紹Elmah, Nlog,大家可以參考一下,或者網路上也有很多資源可以找找。
在那篇文章中,「Override OnException method inside the Controller 」這方法會先判斷Exception是否有被處理,若沒被處理過,就用自己設定的錯誤處理方式(紀錄Log、轉到指定頁面、JsonResult 處理....)。
若之前沒有在Web.config 加上 <customErrors mode="On">,錯誤處理會正常執行,但若加上了<customErrors mode="On">,表示exception在OnException method 之前就已經被捕捉到,所以判斷
1: if (filterContext.ExceptionHandled)
都永遠是true。
所以若要自訂的OnException 發生作用,記得要拿掉<customErrors> 項目。
404 Error 處理
在保哥那本<ASP.NET MVC4開發實戰>書中,有提到找不到Action時的處理方式:複寫Controller 的HandleUnknownAction()。
不過這個方法只限定Action找不到時才會有用... 若連Controller name都有錯,還是會出現預設的404錯誤頁面:
在網路上大部分介紹404處理方式,大都是在web.config加上<customErrors> 項目。
1: <customErrors mode="On">
2: <error redirect="~/Error/Error404" statusCode="404" />
3: </customErrors>
但用這方法,就會跟上面自訂的 OnException() 不和...
所以,若要自訂OnException() ,又不想加上<customErrors> 項目,但卻要處理404錯誤...
請參考下面那篇文章,在Global.asax加上Application_Error()
注意,原始範例中並沒有 Response.ContentType = "text/html"; 這行,那導致404錯誤頁面會以Source Code-純文字Html原始碼方式輸出。
不過討論中有人回覆加上這行就解決了~
http://stackoverflow.com/questions/5226791/custom-error-pages-on-asp-net-mvc3/5229581#5229581
1: protected void Application_Error()
2: {
3: var exception = Server.GetLastError();
4: var httpException = exception as HttpException;
5: Response.Clear();
6: Server.ClearError();
7: var routeData = new RouteData();
8: routeData.Values["controller"] = "Error";
9: routeData.Values["action"] = "Error";
10: routeData.Values["exception"] = exception;
11: Response.ContentType = "text/html";
12: Response.StatusCode = 500;
13: if (httpException != null)
14: {
15: Response.StatusCode = httpException.GetHttpCode();
16: switch (Response.StatusCode)
17: {
18: case 404:
19: routeData.Values["action"] = "PageNotFound";
20: break;
21: }
22: }
23:
24: IController errorsController = new NetFlow2.Controllers.ErrorController();
25: var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
26: errorsController.Execute(rc);
27: }
補充一下:
這兩天開發時,發現網址打錯,竟然不會導到自訂的錯誤頁面,而是秀出跟HttpNotFound()一樣的錯誤頁面:
開了Nlog、Elma的記錄檔,都沒有任何資料...
找了好久,我才想到Error錯誤頁面也有套用Layout,而前幾天剛好在Layout上加了一個ViewData[“使用者”].toString(),當自訂的錯誤頁面套用Layout時,因為ViewData[“使用者”]是null,所以toString()時發生了錯誤!
唉,寫程式習慣不好,又沒辦法一步一步偵錯找出問題,真的只能用猜的...
番外篇:
之前MVC4 由Template 產生出來的Controller,常常可以看到類似這樣一段程式:
if data == null)
{
return HttpNotFound();
}
那時沒有好好讀書,也傻傻的這樣用下去...
當找不到資料時,噴給User看的話結果就是:
這樣一定不行的,使用者一看到這鬼畫面,馬上就會打來K人!
所以現學現賣,加個錯誤處理[HandleError],想讓畫面導到Error Page,「友善地」告訴使用者系統有問題找不到資料,看他們打來罵人會不會溫柔點~
public ActionResult Index()
{
if data == null)
{
return HttpNotFound();
}
}
結果呢...當找不到資料時,還是一樣的錯誤畫面!
這時...若你跟我一樣疑惑,請打自己兩巴掌!清醒點後,再打開MSDN查說明文件。
[HandleError] 是拿來處理錯誤的,也就是當例外發生時,才會有作用。HttpNotFound() 只會回傳一個HttpNotFoundResult 物件(外加把HTTP status code設定為404),又不是丟回錯誤例外,當然不會轉到Error Page!
結論是...大家應該自己寫個Method(),取代HttpNotFound(),當找不到資料時,轉到自訂的訊息頁面。或者寫個錯誤訊息(如使用 ModelState.AddModelError),回到前端畫面通知使用者。(若不想改HttpNotFound(),應該也可以在IIS設定,當捕捉到404錯誤時,要轉到哪個頁面...)