1. 程式人生 > 其它 >asp.net web api 異常捕獲

asp.net web api 異常捕獲

1 向客戶端傳送錯誤訊息

使用throw new HttpResponseException()向客戶端丟擲錯誤資訊。

HttpResponseException包含兩個過載的建構函式,其中一個是建構函式引數型別為HttpResponseMessage,通過其設定狀態碼,錯誤訊息短語以及訊息體內容來向客戶端丟擲比較詳細的錯誤資訊。另一個引數型別為HttpStatusCode,只能設定狀態碼。

2自定義異常過濾器

擴充套件IExceptionFilter來定義異常過濾器。異常過濾器不會捕獲型別為HttpResponseException的異常,下面的異常也無法被異常過濾器捕獲:

1)controller構造器丟擲的異常

2)訊息處理器丟擲的異常

3)路由過程中丟擲的異常

4)響應內容序列化與反序列化過程中丟擲的異常

程式碼示例:

public class CustomExceptionFilterAttribute : ExceptionFilterAttribute 
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            if (context.Exception!=null)
            {
                LogHelper.LogError(context.Exception);
            }
        }
    }

3 擴充套件ExceptionHandler和ExceptionLogger

擴充套件ExceptionHandler可以捕獲大部分異常,包括一些無法被異常過濾器捕獲的異常。但是HttpResponseException型別的異常不會被捕獲。

示例程式碼:

/// <summary>
    /// 自定義的異常處理程式
    /// </summary>
    public class GlobalExceptionHandler : ExceptionHandler
    {
        /// <summary>
        /// 處理異常
        /// </summary>
        /// <param name="context"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override Task HandleAsync(ExceptionHandlerContext context,CancellationToken cancellationToken)
        {
            if (!ShouldHandle(context))
            {
                return Task.FromResult(0);
            }
            context.Result = new ErrorResult
            {
                Request = context.ExceptionContext.Request,
                Content = "呀! 有異常,請聯絡管理員"
            };
            return Task.FromResult(0);
        }
        /// <summary>
        /// 判斷是否應該處理
        /// 後期擴充套件,重寫方法可過濾掉不需處理的異常
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override bool ShouldHandle(ExceptionHandlerContext context)
        {
            return true;
        }
        private class ErrorResult : IHttpActionResult
        {
            public HttpRequestMessage Request { get; set; }
            public string Content { get; set; }
            public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
            {
                HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
                response.Content = new StringContent(Content);
                response.RequestMessage = Request;
                return Task.FromResult(response);
            }
        }
    }
public class GlobalExceptionLogger : ExceptionLogger
    {
        public override Task LogAsync(ExceptionLoggerContext context,CancellationToken cancellationToken)
        {
            if (!ShouldLog(context))
            {
                return Task.FromResult(0);
            }
            if (context.Exception != null)
            {
                string msg = ClientInfoAnalysis.GetClientInfo();
                LogHelper.LogError(context.Exception, msg);
            }
            return Task.FromResult(0);
        }
        /// <summary>
        /// 判斷是否應記錄異常
        /// 後期重寫此方法,可過濾掉不需要記錄的異常資訊
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override bool ShouldLog(ExceptionLoggerContext context)
        {
            if ((context.Exception is System.Web.HttpException))
            {
                return false;
            }
            return true;
        }
}
public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // 載入log4net配置檔案
            LogConfigLoading.Load(AppSettings.Log4netPathForWeb);

            // 載入Web API服務
            config.Services.Replace(typeof(IAssembliesResolver), new ServiceAssembliesResolver(AppSettings.ServicesLocation));

            // 全域性異常資訊處理
            config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());

            // 全域性異常記錄
            config.Services.Add(typeof(IExceptionLogger), new GlobalExceptionLogger());
}
}

4某些異常無法被捕獲的異常

問題描述

對於在服務載入過程中的異常,無法通過異常過濾器,即實現了System.Web.Http.Filters.IExceptionFilter介面的過濾器來捕獲,也不能通過註冊ExceptionLogger來達到目的。解決方法如下:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            try
            {
                config.Services.Replace(typeof(IAssembliesResolver), new ServiceAssembliesResolver(SysSettings.ServicesLocation));
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex);
            }

//其他程式碼
}
}

其中ServiceAssembliesResolver為:

public class ServiceAssembliesResolver : DefaultAssembliesResolver
    {
        //服務外掛路徑
        private string path;
        public ServiceAssembliesResolver(string path):base()
        {
            this.path = path;
        }
        public override ICollection<Assembly> GetAssemblies()
        {
            //獲得已有的服務
            ICollection<Assembly> baseAssemblies = base.GetAssemblies();
            //初始化
            List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
            //載入每一個服務外掛
            foreach (string file in Directory.GetFiles(path, "*.dll"))
            {
                var controllersAssembly = Assembly.LoadFrom(file);
                assemblies.Add(controllersAssembly);
            }

            return assemblies;
        }
    }

但上述方法很可能不起作用,根本原因在於將config.Services.Replace(typeof(IAssembliesResolver), new ServiceAssembliesResolver(SysSettings.ServicesLocation));放入try-catch塊中,若ServiceAssembliesResolver在例項化的時候不丟擲異常,而是當呼叫GetAssemblies時丟擲異常(例如服務外掛儲存資料夾被刪除),此時無法記錄異常。那麼問題就在於GetAssemblies方法何時被呼叫,通過跟蹤程式碼發現Register中的所有程式碼都執行完成才會載入服務。解決辦法是在ServiceAssembliesResolver.GetAssemblies中捕獲異常並記錄下來。