2015-02-10 77 views
2

我建立使用MVC5一個新的Web應用程序,我需要如下:MVC錯誤處理與自定義錯誤消息

  1. 發現錯誤
  2. 登錄文件的細節
  3. 給他們發通過電子郵件
  4. 添加到詳細的自定義信息(例如 記錄的Id我試圖閱讀)
  5. 返回視圖自定義消息給用戶

我已經找到關於HandleErrorAttribute但他們沒有允許具體細節添加到錯誤,也是我發現的信息說,try catch形式給出是大量的信息服務器太重

現在,我有:

控制器:

public partial class HomeController : Controller 
{ 
    private static Logger logger = LogManager.GetCurrentClassLogger(); 

    public virtual ActionResult Index() 
    { 
     try 
     { 
      return View(); 
     } 
     catch (Exception e) 
     { 
      logger.Error("Error in Index: " + e); 
      return MVC.Error.Index("Error in Home Controller"); 
     } 
    } 
} 

我發現這個擴展HandleErrorAttribute似乎完成,但不這樣做我需要的一切:

private bool IsAjax(ExceptionContext filterContext) 
{ 
    return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest"; 
} 

public override void OnException(ExceptionContext filterContext) 
{ 
    if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) 
    { 
     return; 
    } 


    // if the request is AJAX return JSON else view. 
    if (IsAjax(filterContext)) 
    { 
     //Because its a exception raised after ajax invocation 
     //Lets return Json 
     filterContext.Result = new JsonResult(){Data=filterContext.Exception.Message, 
      JsonRequestBehavior=JsonRequestBehavior.AllowGet}; 

     filterContext.ExceptionHandled = true; 
     filterContext.HttpContext.Response.Clear();  
    } 
    else 
    { 
     //Normal Exception 
     //So let it handle by its default ways. 
     base.OnException(filterContext); 

    } 

    // Write error logging code here if you wish. 

    //if want to get different of the request 
    //var currentController = (string)filterContext.RouteData.Values["controller"]; 
    //var currentActionName = (string)filterContext.RouteData.Values["action"]; 
} 
+2

你看過[Elmah](http://www.hanselman.com/blog/ELMAHErrorLoggingModulesAndHandlersForASPNETAndMVCToo.aspx)嗎? – 2015-02-14 00:14:06

回答

4

您的要求最適合於Elmah。非常好的插件用於記錄錯誤。

ELMAH代表錯誤日誌記錄模塊和處理

ELMAH提供了這樣一個高度可插拔的,即使ELMAH的安裝不需要您的應用程序的編譯

ELMAH(錯誤記錄模塊和處理程序)是一個完全可插入的應用程序範圍的錯誤記錄工具。它可以動態添加到正在運行的ASP.NET Web應用程序,甚至是機器上的所有ASP.NET Web應用程序,而無需重新編譯或重新部署。從SCOTT HANSELMAN

的博客

參考只需要ELMAH的二進制文件複製到你的應用程序的bin文件夾和編輯的web.config文件。而已!

您需要將以下內容添加到您的web.config中並進行以下鏈接中描述的其他更改。

<sectionGroup name="elmah"> 
    <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" /> 
    <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" /> 
    <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" /> 
    <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" /> 
</sectionGroup> 

例如設立郵件帳戶

<configuration> 
    <configSections> 
     <sectionGroup name="elmah"> 
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah"/> 
      <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah"/> 
      <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah"/> 
     </sectionGroup> 
    </configSections> 
    <elmah> 
    <errorMail from="[email protected]" to="[email protected]" 
     subject="Application Exception" async="false" 
     smtpPort="25" smtpServer="***" 
     userName="***" password="***"> 
    </errorMail> 
    </elmah> 
<system.web>   
    <customErrors mode="RemoteOnly" defaultRedirect="CustomError.aspx"> 
     <error statusCode="403" redirect="NotAuthorized.aspx" /> 
     <!--<error statusCode="404" redirect="FileNotFound.htm" />--> 
    </customErrors> 
    <httpHandlers> 
     <remove verb="*" path="*.asmx"/> 
     <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 
     <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 
     <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/> 
     <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" /> 
    </httpHandlers> 
    <httpModules> 
     <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 
     <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/> 
     <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" /> 
    </httpModules> 
</system.web> 
</configuration> 

這裏有一些很好的參考鏈接(包含詳細參考安裝ELMAH到您的項目)供您參考。

https://msdn.microsoft.com/en-us/library/aa479332.aspx

https://code.google.com/p/elmah/wiki/MVC

更新

添加到自定義的詳細信息

(例如我想讀取記錄的ID)您可以構建自己的導出f的自定義異常ROM例外。

public class MyException : Exception 
{ 
    public MyException(string message, Exception ex) : base(ex.Message, ex) 
    { 

    } 
} 

,然後使用它像

public virtual ActionResult Index() 
{ 
    try 
    { 
     return View(); 
    } 
    catch (Exception e) 
    { 
     throw new MyException("detailed exception", e); 
    } 
} 
這樣

主要例外是在myexception內被包裹,你可以添加你詳細的自定義異常消息。

返回到視圖自定義消息給用戶

你只需要添加

<system.web> 
    <customErrors mode="On"> 
    </customErrors> 
<sytem.web> 

,並添加Error.cshtml~/View/Shared文件夾 裏面然後每當遇到例外,它會發現Error.cshtml在視圖/共享文件夾內並呈現內容。所以你可以在那裏渲染你的自定義消息。

+0

嗨,Jenish,任何添加自定義消息的例子? – Patrick 2015-02-17 22:58:42

+1

最好使用NuGet Elmah.MVC(和依賴Elmah.CoreLibrary)包。它將複製二進制文件和Web.config條目。 – 2015-02-18 03:54:58

-1

全局錯誤捕捉特殊的信息,你可以使用包含所需信息(id,表名等)的客戶異常。

在HandleErrorAttribute中,「僅」具有httpContext/ExceptionContext和其他靜態信息。

0

爲什麼不創建包含所需錯誤信息的模型,並在需要時將數據綁定到模型?它也將允許您從中創建/返回視圖

+0

嗨,謝謝你的任何消化例子嗎? – Patrick 2015-02-17 22:56:49

3

使用Elmah其他人也建議。我是,並沒有回頭看!

它滿足您的所有需求:

  • 捕獲所有的錯誤,例如400s,500s ...
  • 登錄到文件以及您可以想到的任何其他數據存儲區,例如數據庫,內存,Azure,更多文件格式(XML,CSV),RSS源...
  • 電子郵件錯誤:在Web.config中啓用和配置郵件設置 - 非常簡單。你甚至可以異步發送郵件!
  • 添加自定義代碼 - 在您的情況下添加額外的詳細信息到錯誤
  • 使用您自己的自定義錯誤頁面 - web中的自定義錯誤節點(400s,500s)。配置和自己的錯誤控制器

而且在自定義代碼(上述第2最後一點),據我所知,你有兩個選擇:

1.創建一個自定義的錯誤日誌的實現。

這並不困難。這就是我所做的!

覆蓋默認錯誤日誌數據存儲。例如,以SQL Server數據存儲:

In Web.config 
<elmah> 
    <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="myCn" applicationName="myAppName" /> 
</elmah> 

接下來,創建一類「MySQLServerErrorLog」並從中Elmah.ErrorLog

All,然後所需要的是覆蓋日誌()方法。

public override string Log(Error error) 
     { 

     // You have access to all the error details - joy!  
     = error.HostName, 
     = error.Type, 
     = error.Message, 
     = error.StatusCode 
     = error.User, 
     = error.Source, 

     // Call the base implementation 
    } 

在web.config中,替換默認的(上述)入口點,您的實現:

<elmah> 
    <errorLog type="myProjectName.MySQLServerErrorLog, myProjectName" /> 
</elmah> 

2.您可以通過編程記錄錯誤

使用ErrorSignal類,你可能記錄錯誤而不必引發未處理的異常。

語法: ErrorSignal.FromCurrentContext()。Raise(new NotSupportedException());

例子:一個自定義異常

var customException = new Exception("My error", new NotSupportedException()); 
ErrorSignal.FromCurrentContext().Raise(customException); 

這讓您使用您的定製邏輯,以編程方式登錄任何你需要的選項。

我已經編寫了我的Elmah實例的功能,以便將錯誤記錄到Azure雲存儲表和Blob(錯誤堆棧跟蹤詳細信息)。在我使用Elmah之前,我寫了自己的MVC異常處理機制,它使用HandleErrorAttribute和Application_Error(在Global.asax中)。它的工作,但太笨拙國際海事組織。

+0

嗨,謝謝托馬斯,任何參考實現Elmah與自定義代碼添加在MVC adicional信息? – Patrick 2015-02-17 22:55:59

2

如果是我,我會創建自己的異常處理屬性,它將必需的行爲添加到HandleErrorAttribute的基本實現中。

過去我已經有了相當不錯的結果,有屬性「指向」感興趣的請求的各個部分(正在考慮你說你想記錄具體細節的位置) - 所以你可以使用這些標識符使用反射來拉請求件:

CustomHandleErrorAttribute(["id", "name", "model.lastUpdatedDate"]) 

我用這個方法來保護控制器的操作(確保客戶正在請求他們允許請求的事情) - 例如,一位家長要求他們的孩子的信息,而不是其他人的孩子。或者,你可以有一個配置設置,你可以將處理程序「鏈接」在一起 - 所以很多小處理程序,所有處理非常特定的位,所有工作在相同的請求和請求指針上(如上):

ChainedErrorHandling("emailAndLogFile", ["id", "name", "model.lastUpdatedDate"]) 

其中「emailAndLogFile」創建一個從FilterAttribute繼承的錯誤處理程序鏈,其最後一個在鏈中是標準MVC HandleErrorAttribute。

但是,到目前爲止,最簡單的方法是前者的這些2.

HTH


編輯補充:實例繼承自定義錯誤處理的:

public class CustomErrorAttribute : HandleErrorAttribute 
{ 
    public CustomErrorAttribute(string[] requestPropertiesToLog) 
    { 
     this.requestPropertiesToLog = requestPropertiesToLog; 
    } 

    public string[] requestPropertiesToLog { get; set; } 

    public override void OnException(ExceptionContext filterContext) 
    { 
     var requestDetails = this.GetPropertiesFromRequest(filterContext); 

     // do custom logging/handling 
     LogExceptionToEmail(requestDetails, filterContext); 
     LogExceptionToFile(requestDetails, filterContext); 
     LogExceptionToElseWhere(requestDetails, filterContext);// you get the idea 

     // even better - you could use DI (as you're in MVC at this point) to resolve the custom logging and log from there. 
     //var logger = DependencyResolver.Current.GetService<IMyCustomErrorLoggingHandler>(); 
     // logger.HandleException(requestDetails, filterContext); 

     // then let the base error handling do it's thang. 
     base.OnException(filterContext); 
    } 

    private IEnumerable<KeyValuePair<string, string>> GetPropertiesFromRequest(ExceptionContext filterContext) 
    { 
     // in requestContext is the queryString, form, user, route data - cherry pick bits out using the this.requestPropertiesToLog and some simple mechanism you like 
     var requestContext = filterContext.RequestContext; 
     var qs = requestContext.HttpContext.Request.QueryString; 
     var form = requestContext.HttpContext.Request.Form; 
     var user = requestContext.HttpContext.User; 
     var routeDataOfActionThatThrew = requestContext.RouteData; 

     yield break;// just break as I'm not implementing it. 
    } 

    private void LogExceptionToEmail(IEnumerable<KeyValuePair<string, string>> requestDetails, ExceptionContext filterContext) 
    { 
     // send emails here 
    } 

    private void LogExceptionToFile(IEnumerable<KeyValuePair<string, string>> requestDetails, ExceptionContext filterContext) 
    { 
     // log to files 
    } 

    private void LogExceptionToElseWhere(IEnumerable<KeyValuePair<string, string>> requestDetails, ExceptionContext filterContext) 
    { 
     // send cash to me via paypal everytime you get an exception ;) 
    } 
} 

並在控制器動作,您會加入類似:

[CustomErrorAttribute(new[] { "formProperty1", "formProperty2" })] 
public ActionResult Index(){ 
    return View(); 
} 
+0

嗨,謝謝安德魯,但我有一些理解這個概念的困難。你可以給的任何例子更好地堅持下去嗎? – Patrick 2015-02-17 22:53:53

+0

嗨帕特里克。對不起 - 在重新閱讀我可以看到它爲什麼不明確。所以 - 我們說標準的HandleErrorAttribute做了一些,但不是所有必需的動作 - 我建議(並且在相似的環境中取得了巨大的成功)做了一個新的屬性類,繼承這個屬性類,並使用一旦它做了額外的事情(例如,記錄到文件/電子郵件,同時從請求中逐個獲得細節)。我會用僞示例添加一個額外的答案,也許這會有所幫助。 – 2015-02-18 08:51:41

+0

嗨帕特里克 - 編輯我的答案給你一個屬性的開始。 HTH! – 2015-02-18 09:21:02

1

Fistly您可以定義一個篩選器屬性,你可以在啓動時MVC應用程序的Global.asax中進行註冊,這樣你就可以捕捉在行動調用發生任何錯誤。

注:依賴解決是可變的。我正在使用Castle Windsor這個故事。您可以解析您自己的IOC容器的依賴關係。例如:ILogger依賴。我在行動調用時用於這種刺激注射。 這裏:Windsor Action Invoker

對於示例篩選:

 public class ExceptionHandling : FilterAttribute, IExceptionFilter 
     { 
      public ILogger Logger { get; set; } 

      public void OnException(ExceptionContext filterContext) 
      { 
       Logger.Log("On Exception !", LogType.Debug, filterContext.Exception); 

       if (filterContext.Exception is UnauthorizedAccessException) 
       { 
        filterContext.Result = UnauthorizedAccessExceptionResult(filterContext); 
       } 
       else if (filterContext.Exception is BusinessException) 
       { 
        filterContext.Result = BusinessExceptionResult(filterContext); 
       } 
       else 
       { 
        // : Unhandled Exception 
        Logger.Log("Unhandled Exception ", LogType.Error, filterContext.Exception); 
        filterContext.Result = UnhandledExceptionResult(filterContext); 
       } 
      } 
     } 

這種方式可以捕獲一切,

所以:

private static ActionResult UnauthorizedAccessExceptionResult(ExceptionContext filterContext) 
    { 
     // Send email, fire event, add error messages 
     // for example handle error messages 
     //you can seperate the behaviour by: if (filterContext.HttpContext.Request.IsAjaxRequest()) 
     filterContext.ExceptionHandled = true; 
     filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; 
     filterContext.Controller.TempData.Add(MessageType.Danger.ToString(), filterContext.Exception.Message); 

     //So you can show messages using with TempData["Key"] on your action or views 

     var lRoutes = new RouteValueDictionary(
      new 
       { 
        action = filterContext.RouteData.Values["action"], 
        controller = filterContext.RouteData.Values["controller"] 
       }); 
     return new RedirectToRouteResult(lRoutes); 

    } 

在Global.asax中:

protected void Application_Start() 
     { 
      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
     } 

一個FilterConfig:

public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     filters.Add(new ExceptionHandling()); 
    } 

BusinessException:

public class BusinessException : Exception, ISerializable 
{ 
    public BusinessException(string message) 
     : base(message) 
    { 
     // Add implemenation (if required) 
    } 
} 

,所以你可以在ExceptionHandling類訪問異常消息onException的與filterContext.Exception.Message

你應該在行動使用businessexception之後的任何侵犯控制邏輯。 用這種方法:throw new BusinessException("Message")

+0

嗨,謝謝!你在哪裏將Action的自定義信息傳遞給過濾器,例如ClientId? – Patrick 2015-02-24 11:08:36

+0

不,您可以定義一個Exception類,像BusinessException類(注意:BusinessException類應該從System.Exception派生)因此,當您在操作方法上鍵入'new BusinessException(「Parameter is invalid」); ,businessexception傳遞給'OnException'事件,所以你可以這樣處理。 – 2015-02-24 14:48:29

+0

我編輯了我的答案。 – 2015-02-24 15:16:58