每個網站必備的 ELMAH (Error Logging Modules and Handlers) 模組,在使用 ASP.NET Web API 時卻無法自動套用,也就是說 ASP.NET Web API 執行的過程中發生任何例外,預設都不會自動寫入 ELMAH 指定的儲存區。那是因為 ASP.NET Web API 的主要用途是用來回應呼叫 RESTful API 的用戶端要求,為了不讓用戶端得到不符合 JSON 或 XML 格式的訊息內容,因此所有例外都會被 ApiController 基底類別給攔截,如果我們想在 ASP.NET Web API 實作錯誤紀錄,則必須透過 Action Filter 來設定。
為了讓全站所有 ASP.NET Web API 控制器都能自動取得執行過程中的錯誤紀錄,我們先來定義一個 例外過濾器 (Exception Filters),比較簡單的作法是直接繼承 ExceptionFilterAttribute 屬性類別,並且取名為 ElmahErrorAttribute 以便記憶,程式碼如下:
using System.Web.Http.Filters;
public class ElmahErrorAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Exception != null)
Elmah.ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
base.OnException(actionExecutedContext);
}
}
重點就在於以下這行,將 例外過濾器 取得的例外,寫入到 Elmah 裡:
請注意: Elmah.ErrorSignal.FromCurrentContext().Raise(…) 是 ELMAH 1.2 之後才提供的 API (文件庫)
- Elmah.ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
接著我們開啟 App_Start\WebApiConfig.cs 類別,在 WebApiConfig 類別的 Register 方法開頭的地方,將 ElmahErrorAttribute 註冊到 全域動作過濾器 (Global Action Filter) 裡,範例如下:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(
new ElmahErrorAttribute()
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
如此一來,日後若 ASP.NET Web API 再遇到錯誤,就可以從 ELMAH 查出完整的錯誤訊息與堆疊追蹤資訊了!
有時候遇到比較小的案子,為了方便起見,我會把 ElmahErrorAttribute 類別直接跟 WebApiConfig 放在同一個 C# 檔案裡,以方便管理:
using System.Web.Http;
using System.Web.Http.Filters;
namespace MvcApplication1
{
public class ElmahErrorAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Exception != null)
Elmah.ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
base.OnException(actionExecutedContext);
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(
new ElmahErrorAttribute()
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}
相關連結