• C#程序全局异常处理—WPF和Web API两种模式


    C#程序的全局异常处理,网上搜下资料都是一大堆,我这里最近也是独立做一个B/S结构的小项目, 后面又增加了需求用WPF实现相同的功能,这里将我所使用的全局异常处理方式做一个简短的总结分享。
    Web API项目的全局异常处理
    这种项目下,我们可以直接自定义一个全局异常的过滤器,用来处理全局异常。具体步骤就是在WEB API项目中,定义一个继承自AppExceptionFilterAttribute的类,这个类需要引入命名空间Microsoft.AspNetCore.Mvc.Core.dll,一般来说如果是WEB API或者说MVC的项目,这个会自动帮我们引入或者说VS智能提示帮助我们引入。我们自定义的类只需要重写OnException方法即可,当然现在都推荐使用其异步模式OnExceptionAsync方法,我这里自定义的异常处理类是AppExceptionFilterAttribute,具体代码如下。

    自定义的全局异常处理类AppExceptionFilterAttribute
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Abstractions;
    using Microsoft.AspNetCore.Mvc.Filters;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace WebApi
    {
        [AttributeUsage(AttributeTargets.Method|AttributeTargets.Class,AllowMultiple =false,Inherited =false)]
        public class AppExceptionFilterAttribute:ExceptionFilterAttribute
        {
            //private static ILog log = LogManager.GetLogger(typeof(AppExceptionFilter));//typeof放当前类
            public override void OnException(ExceptionContext actionExecutedContext)
            {
                ActionDescriptor actionDescriptor = actionExecutedContext.ActionDescriptor;
                if (actionDescriptor.EndpointMetadata.Any(m => m.GetType() == typeof(SkipFilterAttribute)))
                {
                    //判断如果操作方法有SkipFilterAttribute特性,则跳过自定义的过滤器的执行
                    base.OnException(actionExecutedContext);
                    return;
                }
    
                Exception ex = actionExecutedContext.Exception;
                if (ex != null)
                {
                    string ControllerName = actionDescriptor.RouteValues["controller"];
                    string ActionName = actionDescriptor.RouteValues["action"];
                    //log.Error(string.Format("Filter捕获到未处理异常:ControllerName={0},ActionName={1}, ExType={2},Source={3},Message={4},Exception={5}", ControllerName, ActionName, ex.GetType(), ex.Source, ex.Message, ex.ToString()));
    
                    JsonResult jsonResult = new JsonResult($"全局Filter已进行错误处理。{ex.Message}");
                    jsonResult.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
    
                    actionExecutedContext.Result = jsonResult;
                }
                actionExecutedContext.ExceptionHandled = true;
            }
        }
    }
    

    在上述代码中,只需要将AppExceptionFilterAttribute标记在方法的头上或者Controller的头上即可,如果在方法头上,当程序执行到此方法出现未处理异常时,会由AppExceptionFilterAttribute来处理。我们可以在AppExceptionFilterAttribute中集成日志组件,将错误的异常日记记录下来。如果在Controller的头上标记此特性,那么针对此Controller中定义的方法,在出现异常时都会进入到AppExceptionFilterAttribute来处理。如果想让某个方法不去使用此异常过滤器来处理。我们还可以加上一个跳过的处理操作,上面的代码中SkipFilterAttribute类就是用来做跳过自定义异常处理的功能,只需要定义个空的特性处理类即可,代码如下:

    跳过自定义异常处理的特性类
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace WebApi
    {
        /// 
        /// 跳过过滤器的操作,定义在方法头上,带有此标记的方法不会去处理过滤
        /// 
        [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
        public class SkipFilterAttribute : Attribute
        {
    
        }
    }
    

    最后贴上一段在Controller中使用的代码示例,这个示例中我是在Controller的头上使用了特性类AppExceptionFilterAttribute,而在接口方法Get3DModelData的头上使用了SkipFilterAttribute特性做标记,因此Get3DModelData出现异常后进入AppExceptionFilterAttribute中检测到方法头上标记了SkipFilterAttribute,会跳过自定义异常处理。

    Controller中使用自定义异常处理
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace WebApi.Controllers
    {
        [AppExceptionFilter]
        [Route("[controller]/[action]")]
        [ApiController]
        public class Dicom3DController : Controller
        {
            [SkipFilterAttribute]
            [HttpPost]
            public IActionResult Get3DModelData([FromBody]Dicom3DRequest request)
            {
                Dicom3DServer server = new Dicom3DServer();
                D3Model model= server.Get3DData(request.StudyId, request.SeireisId,request.CurrentBatch);
                
                return Json(model);
            }
    
            public IActionResult GetUserInfo()
            {
                return Json("success");
            }
        }
    }
    

    WPF全局异常处理
    WPF程序的全局异常处理就更简单了,我们创建的WPF的项目入口类都是继承自System.Windows.Application类,默认创建的项目的入口类名为APP,我们在这个类中定义一个构造函数,在其中绑定三个异常事件即可,具体为什么需要三个,直接可以看我代码的注释即可。

    WPF的全局异常处理
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Data;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows;
    
    namespace WpfApp1
    {
        /// 
        /// Interaction logic for App.xaml
        /// 
        public partial class App : Application
        {
            public App()
            {
                //当应用程序引发但未处理异常时出现,UI线程的异常,无法捕获多线程异常
                Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
    
                //当某个异常未被捕获时出现,Thread多线程和UI线程都可以捕获
                AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    
                //未被观察到的Task多线程异常
                TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
            }
    
            private void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
            {
                e.Handled = true; //使用e.Handled能防止程序崩溃
                MessageBox.Show($"Current_DispatcherUnhandledException:" + e.Exception.Message);
            }
            private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
            {
                MessageBox.Show($"CurrentDomain_UnhandledException:" + (e.ExceptionObject as Exception).Message);
            }
            private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
            {
                MessageBox.Show($"TaskScheduler_UnobservedTaskException:" + e.Exception.Message);
            }
    
        }
    }
    
  • 相关阅读:
    树状数组及扩展
    谈谈构建有效数据治理策略的十条建议
    逻辑漏洞基础
    一文详解 requests 库中 json 参数和 data 参数的用法
    从零开始配置 vim(14)——目录管理插件
    Raft 共识算法
    OpenGL 坐标系统详解
    序列超图的下一项推荐 笔记
    大宋咨询数据研究在汽车新品上市中的核心作用
    Java 修饰符2
  • 原文地址:https://www.cnblogs.com/huangqian/p/18036541