Asp.NetCore依赖注入和管道方式的异常处理及日志记录

原文:Asp.NetCore依赖注入和管道方式的异常处理及日志记录

前言

????在业务系统,异常处理是所有开发人员必须面对的问题,在一定程度上,异常处理的能力反映出开发者对业务的驾驭水平;本章将着重介绍如何在 WebApi 程序中对异常进行捕获,然后利用 Nlog 组件进行记录;同时,还将介绍两种不同的

异常捕获方式:管道捕获/服务过滤;通过本练习,将学习到如何捕获异常、处理异常跳转、记录异常信息。

1. 搭建框架

????首先,创建一个 WebApi 项目,选择 Asp.Net Core Web 应用程序;

1.1 进一步选择 Api 模板,这里使用的 .netcore 版本为 2.1

  • 取消勾选 “启用 Docker 支持(E)” 和 “为 Https 配置(C)”,点击确定,得到一个完整的 WebApi 项目框架,如图

1.2 直接按 F5 运行项目,一切正常,程序启动后进入默认路由调用,并输出结果

2. 异常路由

2.1 一切看起来都非常正常和美好,但,祸之福所倚;接下来我们在 接口 Get() 中人为的制造一点麻烦。
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            throw new Exception("出错了.....");

            return new string[] { "value1", "value2" };
        }
2.2 这是由于项目配置了运行环境变量 ASPNETCORE_ENVIRONMENT=Development 后,Startup.cs 中配置了开发环境下,使用系统默认页,所以我们才可以看到上面的异常信息

如果你把环境变量设置为 ASPNETCORE_ENVIRONMENT=Production ,你会发现,在异常发生的时候,你得到了一个空白页。

3. 异常处理方式一:服务过滤

????在传统的 Asp.Net MVC 应用程序中,我们一般都使用服务过滤的方式去捕获和处理异常,这种方式非常常见,而且可用性来说,体验也不错,幸运的是 Asp.Net Core 也完整的支持该方式,接下来创建一个全局异常处理类 CustomerExceptionFilter

public class CustomerExceptionFilter : Attribute, IExceptionFilter
{
    private readonly ILogger logger = null;
    private readonly IHostingEnvironment environment = null;
    public CustomerExceptionFilter(ILogger<CustomerExceptionFilter> logger, IHostingEnvironment environment)
    {
        this.logger = logger;
        this.environment = environment;
    }

    public void OnException(ExceptionContext context)
    {
        Exception exception = context.Exception;
        string error = string.Empty;

        void ReadException(Exception ex)
        {
            error += string.Format("{0} | {1} | {2}", ex.Message, ex.StackTrace, ex.InnerException);
            if (ex.InnerException != null)
            {
                ReadException(ex.InnerException);
            }
        }

        ReadException(context.Exception);
        logger.LogError(error);

        ContentResult result = new ContentResult
        {
            StatusCode = 500,
            ContentType = "text/json;charset=utf-8;"
        };

        if (environment.IsDevelopment())
        {
            var json = new { message = exception.Message, detail = error };
            result.Content = JsonConvert.SerializeObject(json);
        }
        else
        {
            result.Content = "抱歉,出错了";
        }
        context.Result = result;
        context.ExceptionHandled = true;
    }
}
3.1 CustomerExceptionFilter 继承自 IExceptionFilter 接口,并实现 void OnException(ExceptionContext context) 方法,在 CustomerExceptionFilter

构造方法中,定义了两个参数,用于记录异常日志和获取程序运行环境变量

    private readonly ILogger logger = null;
    private readonly IHostingEnvironment environment = null;
    public CustomerExceptionFilter(ILogger<CustomerExceptionFilter> logger, IHostingEnvironment environment)
    {
        this.logger = logger;
        this.environment = environment;
    }
3.2 在接下来的 OnException 方法中,利用 environment 进行产品环境的判断,并使用 logger 将日志写入硬盘文件中,为了将日志写入硬盘,

需要引用 Nuget 包 NLog.Extensions.Logging/NLog.Web.AspNetCore ,并在 Startup.cs 文件的 Configure 方法中添加扩展

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory factory)
        {
            // 将 NLog
            factory.AddConsole(Configuration.GetSection("Logging"))
                   .AddNLog()
                   .AddDebug();

            var nlogFile = System.IO.Path.Combine(env.ContentRootPath, "nlog.config");
            env.ConfigureNLog(nlogFile);

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
        }
3.3 上面的代码读取了配置文件 nlog.config 并设置为 NLog 的配置,该文件定义如下
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" internalLogLevel="info">

  <!-- Load the ASP.NET Core plugin -->
  <extensions>
    <add assembly="NLog.Web.AspNetCore"/>
  </extensions>

  <!-- Layout: https://github.com/NLog/NLog/wiki/Layout%20Renderers -->
  <targets>
    <target xsi:type="File" name="errorfile" fileName="/data/logs/logfilter/error-${shortdate}.log" layout="${longdate}|${logger}|${uppercase:${level}}|  ${message} ${exception}|${aspnet-Request-Url}" />
    <target xsi:type="Null" name="blackhole" />
  </targets>

  <rules>
    <logger name="Microsoft.*" minlevel="Error" writeTo="blackhole" final="true" />
    <logger name="*" minlevel="Error" writeTo="errorfile" />
  </rules>
</nlog>
3.4 为了在 WebApi 控制器中使用 CustomerExceptionFilter 过滤器,我们还需要在 Startup.cs 将 CustomerExceptionFilter 注入到容器中
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // 将异常过滤器注入到容器中
            services.AddScoped<CustomerExceptionFilter>();

            services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        }
3.5 最后,在控制器 ValuesController 中应用该异常过滤器
    [ServiceFilter(typeof(CustomerExceptionFilter))]
    [Route("api/[controller]"), ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            throw new Exception("出错了.....");
            return new string[] { "value1", "value2" };
        }
    }
3.6 现在,按 F5 启动程序,如预期所料,报错信息被 CustomerExceptionFilter 捕获,并转换为 json 格式输出

3.7 同时,NLog 组件也将日志信息记录到了硬盘中

4. 异常处理方式二:中间件捕获

接下来利用 .NetCore 的管道模式,在中间件中对异常进行捕获,首先,创建一个中间件

public class ExceptionMiddleware
{
    private readonly RequestDelegate next;
    private readonly ILogger logger;
    private IHostingEnvironment environment;

    public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger, IHostingEnvironment environment)
    {
        this.next = next;
        this.logger = logger;
        this.environment = environment;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await next.Invoke(context);
            var features = context.Features;
        }
        catch (Exception e)
        {
            await HandleException(context, e);
        }
    }

    private async Task HandleException(HttpContext context, Exception e)
    {
        context.Response.StatusCode = 500;
        context.Response.ContentType = "text/json;charset=utf-8;";
        string error = "";

        void ReadException(Exception ex)
        {
            error += string.Format("{0} | {1} | {2}", ex.Message, ex.StackTrace, ex.InnerException);
            if (ex.InnerException != null)
            {
                ReadException(ex.InnerException);
            }
        }

        ReadException(e);
        if (environment.IsDevelopment())
        {
            var json = new { message = e.Message, detail = error };
            error = JsonConvert.SerializeObject(json);
        }
        else
            error = "抱歉,出错了";

        await context.Response.WriteAsync(error);
    }
}
4.1 代码比较简单,在管道中使用 try/catch 进行捕获异常

创建 HandleException(HttpContext context, Exception e) 处理异常,判断是 Development 环境下,输出详细的错误信息,非 Development 环境仅提示调用者“抱歉,出错了”,同时使用 NLog 组件将日志写入硬盘;同样,在 Startup.cs 中将 ExceptionMiddleware 加入管道中

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory factory)
        {
            // 将 NLog
            factory.AddConsole(Configuration.GetSection("Logging"))
                   .AddNLog()
                   .AddDebug();

            var nlogFile = System.IO.Path.Combine(env.ContentRootPath, "nlog.config");
            env.ConfigureNLog(nlogFile);

            // ExceptionMiddleware 加入管道
            app.UseMiddleware<ExceptionMiddleware>();

            //if (env.IsDevelopment())
            //{
            //    app.UseDeveloperExceptionPage();
            //}

            app.UseMvc();
        }
4.2 一切就绪,按 F5 运行程序,网页中输出了期望中的 json 格式错误信息,同时 NLog 组件也将日志写入了硬盘

结语

在本例中,通过依赖注入和管道中间件的方式,演示了两种不同的全局捕获异常处理的过程;值得注意到是,两种方式对于 NLog 的使用,都是一样的,没有任何差别,代码无需改动;实际项目中,也是应当区分不同的业务场景,输出不同的日志信息,不管是从安全或者是用户体验友好性上面来说,都是非常值得推荐的方式,全局异常捕获处理,完全和业务剥离。

源码下载

https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.LogFilter

原文地址:https://www.cnblogs.com/lonelyxmas/p/11269224.html

时间: 2024-11-07 02:36:35

Asp.NetCore依赖注入和管道方式的异常处理及日志记录的相关文章

Spring第七弹—依赖注入之注解方式注入及编码解析@Resource原理

    注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果. 手工装配依赖对象  手工装配依赖对象,在这种方式中又有两种编程方式 在xml配置文件中,通过在bean节点下配置,上边博客已经讲解,再次不在缀余. 在java代码中使用@Autowired或@Resource注解方式进行装配.但我们需要在xml配置文件中配置以下信息: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <be

几个步骤轻松搞定ASP.NET 依赖注入。

http://www.it165.net/pro/html/201407/17685.html 我在网上看到了这篇文章,这边文章主要说的方法就是通过读取配置文件来解决依赖注入的问题.但是每次新建一个依赖注入的类,就要改一下配置文件,这显然不符合设计模式中的开闭原则.先来看看一般的解法. //var container = new UnityContainer(); //container.RegisterType(typeof(IRegisterService), typeof(Register

依赖注入的实现方式:设值注入和构造方法注入

SSH开发时,一般将项目分成3层:web层.service层和dao层.开发的基本流程是先定义dao接口,然后实现该接口,在定义同类型的service接口,再实现service接口(此时使用dao接口注入),接着再从web层去调用service层. dao层完成的是底层的数据库操作,service层则完成纯粹的业务逻辑,该层中的数据操作部分是通过注入的dao层来实现的.service层的作用是将从dao层取得的数据做更贴近业务的实现,dao层则只实现对数据的增.删.改.查操作.使用这种分层方式更

依赖注入的实现方式

1.构造方法注入 顾名思义,构造方法注入,就是被注入对象可以通过在其构造方法中声明依赖对象的参数列表,让外部(通常是IoC容器)知道它需要哪些依赖对象. IoC Service Provider 会检查被注入对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象.同一个对象是不可能被构造两次的,因此,被注入对象的构造乃至其整个生命周期,应该是由IoC Service Provider 来管理的. 构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用. 2.sett

依赖注入三种方式(待完成)

1.set方法注入 通过setter方法注入:类中一定要提供相应的setXX方法,才能执行成功 <!-- 通过属性注入(setter方法) name表示类中的属性名,类中必须有对应的set方法--> <bean name="user1" class="cn.itcast.spring.h_beanproperty.User"> <property name="id" value="1">&l

Spring IOC - 控制反转(依赖注入) - 创建对象的方式

a.通过类的无参构造方法创建对象 在入门案例中就是这种方式.当用最普通的方式配饰一个<bean>时,默认就是采用类的 无参构造创建对象.在Spring容器初始化时,通过<bean>上配置的class属性反射的到字 节码对象,通过newInstance()创建对象. Class c = Class .forName("类的全路径名称") Object obj = c.newInstance() 这种方式下Spring创建对象,必须有无参的构造,否则无法通过反射创建

Asp.NETCore轻松学系列阅读指引目录

前言 耗时两个多月,坚持写这个入门系列文章,就是想给后来者更好更快的上手体验,这个系列可以说是从入门到进阶,适合没有 .NETCore 编程经验到小白同学,也适合从 .NET Framework 迁移到 .NETCore 的朋友. 本系列从安装环境开始,到认识各种配置文件.然后学习了自定过滤器实现.日志监视.异步任务.多线程.缓存使用.网络通讯.单元测试.常规部署到容器化部署等一系列等文章,每一篇文章都配置了示例代码Demo,大家可以通过每篇文章的末尾找到下载示例代码的链接. 目前,所有的示例代

学习ASP.NET Core,你必须了解无处不在的“依赖注入”

ASP.NET Core的核心是通过一个Server和若干注册的Middleware构成的管道,不论是管道自身的构建,还是Server和Middleware自身的实现,以及构建在这个管道的应用,都需要相应的服务提供支持,ASP.NET Core自身提供了一个DI容器来实现针对服务的注册和消费.换句话说,不只是ASP.NET Core底层框架使用的服务是由这个DI容器来注册和提供,应用级别的服务的注册和提供也需要以来这个DI容器,所以正如本文标题所说的——学习ASP.NET Core,你必须了解无

asp.net core 系列之Dependency injection(依赖注入)

这篇文章主要讲解asp.net core 依赖注入的一些内容. ASP.NET Core支持依赖注入.这是一种在类和其依赖之间实现控制反转的一种技术(IOC). 一.依赖注入概述 1.原始的代码 依赖就是一个对象的创建需要另一个对象.下面的MyDependency是应用中其他类需要的依赖: public class MyDependency { public MyDependency() { } public Task WriteMessage(string message) { Console