1. 程式人生 > >[譯]MVC網站教程(二):異常管理

[譯]MVC網站教程(二):異常管理

介紹

MVC網站教程”系列的目的是教你如何使用 ASP.NET MVC 建立一個基本的、可擴充套件的網站。

2)MVC網站教程(二):異常管理

主要講解如何去建立一個多語言MVC網站,同時也講解了使用者認證和註冊機制的實現。使用了微軟的Entity Framework框架和LINQ查詢技術。

系列的第二篇文章(即本文),制定異常管理規則並在ASP.NET MVC網站中實現異常管理,還提供一些通用的日誌記錄和異常管理的原始碼。這些原始碼不僅可以在任何ASP.NET網站中被重用(或經過比較小的改動適用),而且可以重用到任何.NET專案中。

MVC網站教程”系列的示例網站是採用增量式和迭代式軟體過程開發的,這意味著系列中每一篇博文會在前一篇的解決方案中新增更多的功能,所以本文提供的示例下載只包含系列第一篇和第二篇中所述的功能點。

在開發軟體解決方案中異常管理是非常重要的。如果忽視或者以錯誤方式實現異常管理,將嚴重影響軟體解決方案的質量和效能。

軟體環境

1..NET 4.0 Framework

2.Visual Studio 2010(or Express edition)

3.ASP.NET MVC 4.0

4.SQL Server 2008 R2(or Express Edition version 10.50.2500.0, or higher version)

在執行示例程式碼之前

在執行示例程式碼之前,你應該做下面事情:

1.首先使用“管理員身份”執行CreateEventLogEntry控制檯專案程式產生的

exe,用來在事件日誌中建立“MVC Basic”事件源。(EventLog在寫日誌時會建立類別預設為“應用程式”指定名稱的事件源。但是ASP.NET網站沒有足夠的許可權來建立事件源)

2.在你的SQL Server伺服器中建立一個名為MvcBasicSite的資料庫,然後用我提供的MvcBasicSiteDatabase.bak檔案進行資料庫還原。

3.修改MVC應用程式示例的Web.config配置檔案中的連結字串。

本博文示例下載:

MVC網站中的異常管理規則

在每個軟體專案中,在專案開發的開始階段,團隊應該定義軟體開發中必須遵守的規則。這些規則中應該包含異常管理規則。

在本篇中,我將描述示例MVC解決方案中使用的異常管理規則,並且這些規則可以作為實際專案的參考。能在任何ASP.NET網站中被重用(或經過比較小的改動適用),而且可以用重用到任何.NET專案中。

一般的異常管理機制都是使用trycatchfinally…或者using語句來管理執行可能會失敗的操作(如:訪問資料庫表、從檔案系統訪問檔案、使用記憶體分配、傳送電子郵件等等),以合理的方式處理失敗並且釋放被佔用的資源。注意,.NET Framework框架、第三方類庫、在程式程式碼中使用throw關鍵字都能產生異常。

在示例MVC網站中使用的異常管理規則有:

1.使用finally塊來釋放各種不可釋放的資源,這些資源是沒有實現IDisposable介面的但是又需要一些釋放操作,比如:close()掉在try塊中開啟的Stream或檔案。

2.訪問可釋放資源時可能會產生異常,不要忘記使用trycatchfinally…或using語句來釋放佔用的資源。

3.不要在不需要的地方使用trycatch…吞掉異常,可以考慮使用ifelse…語句避免異常發生從而提高程式效能。比如,在可能為空的物件上執行任何操作之前做非空判斷,能顯著的提高應用程式效能。

4.不要重複catch和重複throw相同型別的異常。

5.為整個解決方案定義一個派生自ApplicationException類的新異常類。在示例程式中,我們定義了BasicSiteException來標識是資料庫或邏輯層產生的異常。

6.為整個解決方案提供一個公用的日誌類,該類將異常資訊寫到Windows系統的“事件日誌”服務中。在本示例中這個類命名為MvcBasicLog

7.捕獲資料庫操作或邏輯層中產生的異常,僅在需要新增更多資訊的時候使用MvcBasicException類再次丟擲異常。注意不要忘記將原始異常做為再次丟擲異常的InnerException屬性值。

8.預期的異常應該在使用者介面層被捕獲並處理,然後異常資訊(訊息和堆疊跟蹤)必須使用MvcBasicLog類寫入Windows的事件日誌中,並且提供一個友好的錯誤資訊顯示在當前頁面給使用者檢視。

9.管理未經授權訪問的異常,來防止使用者在未經過身份驗證或沒有對應的訪問許可權時,訪問站點的主要功能。

10.注意,不要忘記在使用者介面層處理非預期異常,同樣,錯誤資訊也必須寫進Windows的“事件日誌”中,同時在錯誤頁面(Error.cshtml)上顯示一條友好的錯誤資訊。

11.通過使用適當的方法來避免產生不必要的未處理異常。比如,在本示例中使用FirstOrDefault()而不是First(),然後判斷是否為null,就像下面程式碼:(First()在對空序列操作時,會丟擲異常。而FirstOrDefault()在操作空序列時會返回default(TSource)----可參見:

if (ModelState.IsValid)
{
    //
    // Verify the user name and password.
    //
    User user = _db.Users.FirstOrDefault(item => item.Username.ToLower() == 
      model.Username.ToLower() && item.Password == model.Password);
    if (user == null)
    {
        ModelState.AddModelError("", Resources.Resource.LogOnErrorMessage);
        //
        return View(model);
    }
    else
    {
        //
        // User logined succesfully ==> create a new site session!
        //
        FormsAuthentication.SetAuthCookie(model.Username, false);
        //
        SiteSession siteSession = new SiteSession(_db, user);
        Session["SiteSession"] = siteSession; // Cache the user login data!
        //
        // Log a message about the user LogOn.
        //
        MvcBasicLog.LogMessage(string.Format("LogOn for user: {0}", siteSession.Username));
        //
        // Redirect to Home page.
        //
        return RedirectToAction("Index", "Home");
    }
}

將訊息和異常寫入日誌

在任何軟體解決方案中,將軟體使用過程中產生的資訊、錯誤、異常資料存入日誌中是非常重要的。這樣,這些資訊和異常資料就能用於日後訪問和資料分析。在Windows系統中儲存日誌訊息最適合的地方是 Windows Event Log 服務。

MvcBasicLog類是一個公用類,用於將訊息和異常資訊寫入Windows Event Log服務。

     image

如上面類圖中所見,這個類包含了一系列公共的靜態方法用於在Windows event log 服務中記錄訊息、錯誤、異常資訊。所有的訊息將被寫入到同一個日誌源(註冊的日誌源名存在_logSource靜態成員中),並且有些方法提供一個類別字串引數,讓使用者能指定訊息的字首。

所有這些公共方法都使用AddLogLine私有方法來將訊息寫到事件日誌源中。

private static void AddLogLine(string logMessage, bool isError)
{
    EventLog log = new EventLog();
    log.Source = _logSource;
    //
    try
    {
        log.WriteEntry(logMessage, (isError ? EventLogEntryType.Error : EventLogEntryType.Information));
    }
    catch (System.Security.SecurityException ex)
    {
        //
        // In Web app you do not have right to create event log source and
        // the log source must be created first by using the provided CreateEventLogEntry project!
        //
        throw new ApplicationException("You must create the event log entry " + 
          "for our source by using CreateEventLogEntry project!", ex);
    }
    catch
    {
        //
        // The log file is to large, so clear it first.
        //
        log.Clear();
        log.WriteEntry(logMessage, (isError ? EventLogEntryType.Error : EventLogEntryType.Information));
    }
    //
    log.Close();
}

我們可以使用