Asp.net Core 系列之--4.事务、日志及错误处理

摘要:
川Going 2019-11-17最初旨在将事务处理、日志处理、错误处理、授权和身份验证结合起来。我想结合用户定义的权限引入授权和身份验证。这里的所有权限可能都太长,因此将在下一节中介绍这些权限。在前面的示例中,将NLog.Web.AspNetCore的Nuget包添加到WebApi项目中,并添加以下配置:简要介绍配置信息。“目标”配置每个输出配置。这里我有三个输出:database、allfile和ownfile,它们分别表示到数据库的输出和相应路径的日志文件。

ChuanGoing 2019-11-17

    这篇原本想把事务处理、日志处理、错误处理、授权与鉴权一并介绍完的,授权和鉴权我想结合自定义权限来介绍,全部放到这里篇幅可能太长,因此权限部分将会在下篇来介绍。先说下我接下来的打算把,下篇将介绍权限控制,结合Oauth2.0和OpenId(OIDC)以及自定义权限来介绍;完了后会结合之前所介绍的基础来实现一个简单的电商网站,当然是利用领域驱动设计来实现。我的这个系列的主题就是领域驱动设计,实现简单电商网站时将会深入的讲解下领域的划分原则及领域服务的场景,中间可能会尝试部分业务实现事件驱动。

本篇学习曲线:

1.日志记录

2.错误处理

3.事务处理

日志记录

    NLog是一个记录日志组件,和log4net一样被广泛使用,它可以将日志保存到文本文件、CSV、控制台、VS调试窗口、数据库等。在之前例子中的WebApi项目中添加NLog.Web.AspNetCore的Nuget包,并添加如下配置:

Asp.net Core 系列之--4.事务、日志及错误处理第1张

   简单介绍下配置信息,“targets”配置每个输出配置,我这里有3个输出:database、allfile、ownfile,分别表示输出到数据库和对应路径的日志文件下。

"rules"规则配置了4条:

1.将Debug以上级别(含)信息输出到allfile

2.忽略Microsoft.*开头的信息(对应的输出没有配置到任何文件),此配置一般忽略即可

3.将Debug以上级别(含)信息输出到ownfile(注意这里配置和allfile一样,一般配置级别高点的日志信息)

4.将Warn以上级别(含)信息输出到数据库

完了后,在Program.cs Main方法里面注册NLog:

var logger = NLogBuilder.ConfigureNLog($"Nlog.config").GetCurrentClassLogger();
            try
            {
                CreateWebHostBuilder(args).Build().Run();
            }
            catch (Exception ex)
            {
                logger.Error(ex, "Stopped program because of exception");
                throw ex;
            }
            finally
            {
                NLog.LogManager.Shutdown();
            }

注意不要忘了启用NLog组件使之生效

Asp.net Core 系列之--4.事务、日志及错误处理第2张

在OrderController的Add方法中加入以下代码:

Asp.net Core 系列之--4.事务、日志及错误处理第3张

用postman简单测试下,我们可以看到执行目录中多出来了日志信息

 Asp.net Core 系列之--4.事务、日志及错误处理第4张

错误处理

  这里一般我们关心的错误大概有两类:

1.内部错误,即通过框架(Mvc)管道准确的传入到内部系统中并发生错误的此类信息

2.框架(Mvc)执行管道的某些中间件时发生的错误或被中间件禁止继续访问的请求

  因此,定义如下3个类:

Asp.net Core 系列之--4.事务、日志及错误处理第5张Asp.net Core 系列之--4.事务、日志及错误处理第6张
public class InnerException : Exception
    {
        /// <summary>
        /// 内部错误代码
        /// </summary>
        public int? ErrorCode { get; }

        public InnerException(int errorCode) : base()
        {
            ErrorCode = errorCode;
        }

        public InnerException(int errorCode, string message) : base(message)
        {
            ErrorCode = errorCode;
        }

        public InnerException(int code, string message, Exception exception) : base(message, exception)
        {
            ErrorCode = code;
        }
    }
InnerException
Asp.net Core 系列之--4.事务、日志及错误处理第7张Asp.net Core 系列之--4.事务、日志及错误处理第8张
public class MessageCodes
    {
        #region 公用

        /// <summary>
        /// 成功
        /// </summary>
        public const int Success = 20101000;
        /// <summary>
        /// 警告
        /// </summary>
        public const int Warning = 20102000;
        /// <summary>
        /// 错误
        /// </summary>
        public const int Error = 20103000;
        /// <summary>
        /// 数据验证错误
        /// </summary>
        public const int DataValidationError = 20104000;
        /// <summary>
        /// 数据不存在
        /// </summary>
        public const int DataNotFound = 20105000;
        /// <summary>
        /// 非法的数据状态
        /// </summary>
        public const int IllegalState = 20106000;
        /// <summary>
        /// 参数无效
        /// </summary>
        public const int InvalidParams = 20107000;
        /// <summary>
        /// 输入非法
        /// </summary>
        public const int IllegalInput = 20108000;
        /// <summary>
        /// 鉴权成功
        /// </summary>
        public const int AuthSuccess = 20109000;

        #endregion

    }
MessageCodes
Asp.net Core 系列之--4.事务、日志及错误处理第9张Asp.net Core 系列之--4.事务、日志及错误处理第10张
 public class WebException: InnerException
    {
        public HttpStatusCode HttpStatus { get; set; }

        public HttpRequest Request { get; private set; }

        public WebException(HttpStatusCode httpStatus, int errorCode, string message)
             : base(errorCode, message)
        {
            HttpStatus = httpStatus;
        }

        public WebException(HttpStatusCode httpStatus, int errorCode, string message, HttpRequest request)
            : this(httpStatus, errorCode, message)
        {
            Request = request;
        }

        public WebException(int errorCode, string message)
            : base(errorCode, message)
        {
            HttpStatus = HttpStatusCode.BadRequest;
        }
    }
WebException

通过Aop,很方便就可以实现错误信息的处理:

Asp.net Core 系列之--4.事务、日志及错误处理第11张Asp.net Core 系列之--4.事务、日志及错误处理第12张
public class ExceptionFilter : IExceptionFilter
    {
        private readonly ILogger<ExceptionFilter> _logger;

        public ExceptionFilter(ILogger<ExceptionFilter> logger)
        {
            _logger = logger;
        }

        public void OnException(ExceptionContext context)
        {
            _logger.LogError(context.Exception, context.Exception.Message);

            #region Ioc/automapper等中间件对错误信息进行了包装,需要解包

            //web错误:验证/鉴权等
            var webException = GetException<Base.Exceptions.WebException>(context.Exception);
            if (webException != null)
            {
                context.Result = new JsonResult(new
                {
                    ErrorCode = webException.ErrorCode ?? MessageCodes.Error,
                    webException.Message
                })
                {
                    StatusCode = (int)webException.HttpStatus
                };
                return;
            }
            //内部错误
            var exception = GetException<InnerException>(context.Exception);
            if (exception != null)
            {
                context.Result = new JsonResult(new
                {
                    ErrorCode = exception.ErrorCode ?? MessageCodes.Error,
                    exception.Message
                })
                {
                    StatusCode = (int)HttpStatusCode.InternalServerError
                };
                return;
            }

            #endregion
        }

        private TException GetException<TException>(Exception exception)
          where TException : Exception
        {
            if (exception == null)
            {
                return null;
            }
            if (exception is TException tException)
            {
                return tException;
            }
            else
            {
                return GetException<TException>(exception.InnerException);
            }
        }
    }
ExceptionFilter

同时,Startup.cs的ConfigureServices中注册一下:

services.AddMvc(mvcOptions =>
                {
                    mvcOptions.Filters.Add<ExceptionFilter>();
                })

即完成了错误信息并且错误信息会写入相应配置的输出中。

事务处理

  UnitOfWork又称工作单元,为了保证数据操作完整性,我们将处理数据的的操作统一放在一个事务中,我们这里利用UnitOfWork来实现事务处理。

首先定义IUnitOfWork及UnitOfWork实现:

Asp.net Core 系列之--4.事务、日志及错误处理第13张Asp.net Core 系列之--4.事务、日志及错误处理第14张
 public interface IUnitOfWork
    {
        void Begin(IsolationLevel level = IsolationLevel.Unspecified);
        void SaveChanges();
        void Failed();
    }
View Code
Asp.net Core 系列之--4.事务、日志及错误处理第15张Asp.net Core 系列之--4.事务、日志及错误处理第16张
public class UnitOfWork : IUnitOfWork
    {
        private ITransactionRepository _repository;

        public UnitOfWork(ITransactionRepository repository)
        {
            _repository = repository;
        }

        public virtual void Begin(IsolationLevel level = IsolationLevel.Unspecified)
        {
            _repository.BeginTransaction(level);
        }

        public virtual void SaveChanges()
        {
            _repository.Commit();
        }

        public virtual void Failed()
        {
            _repository.Rollback();
        }
    }
View Code

其中,UnitOfWork依赖于ITransactionRepository的实现:

Asp.net Core 系列之--4.事务、日志及错误处理第17张Asp.net Core 系列之--4.事务、日志及错误处理第18张
public interface ITransactionRepository
    {
        /// <summary>
        /// 打开事务
        /// </summary>
        /// <param name="level"></param>
        void BeginTransaction(IsolationLevel level = IsolationLevel.Unspecified);
        /// <summary>
        /// 提交事务
        /// </summary>
        void Commit();
        /// <summary>
        /// 事务回滚
        /// </summary>
        void Rollback();
    }
ITransactionRepository

利用DapperRepository继承ITransactionRepository并实现:

Asp.net Core 系列之--4.事务、日志及错误处理第19张Asp.net Core 系列之--4.事务、日志及错误处理第20张
public virtual void BeginTransaction(IsolationLevel level = IsolationLevel.Unspecified)
        {
            DbContext.BeginTransaction(level);
        }

        public virtual void Commit()
        {
            DbContext.Commit();
        }

        public virtual void Rollback()
        {
            DbContext.RollBack();
        }
View Code

基本功能实现后,如何使用呢?这里还是需要利用Aop:

Asp.net Core 系列之--4.事务、日志及错误处理第21张Asp.net Core 系列之--4.事务、日志及错误处理第22张
public class UnitOfWorkAttribute : AbstractInterceptorAttribute
    {
        public override Task Invoke(AspectContext context, AspectDelegate next)
        {
            if (context.Implementation is IApplicationService applicationService)
            {
                var uow = applicationService.UnitOfWork;
                uow.Begin();
                var aspectDelegate = next(context);
                if (aspectDelegate.Exception != null)
                {
                    uow.Failed();
                    throw aspectDelegate.Exception;
                }
                else
                {
                    uow.SaveChanges();
                    return aspectDelegate;
                }
            }
            else
            {
                return next(context);
            }
        }
    }
UnitOfWorkAttribute

因此,我们还需要在Application项目中添加如下代码:

Asp.net Core 系列之--4.事务、日志及错误处理第23张Asp.net Core 系列之--4.事务、日志及错误处理第24张
 public class ServiceBase<TEntity, TPrimaryKey> : IApplicationService
        where TEntity : class, IEntity<TPrimaryKey>
    {
        protected IMapper Mapper { get; private set; }
        public virtual IUnitOfWork UnitOfWork { get; private set; }

        public ServiceBase(IComponentContext container, ICommandRepository<TEntity, TPrimaryKey> repository)
        {
            Mapper = container.Resolve<IMapper>();
            UnitOfWork = container.Resolve<IUnitOfWork>(new TypedParameter(typeof(ITransactionRepository), repository));
        }
    }
ServiceBase

Application中的每个服务去继承上面的ServiceBase,因此每个Application服务都具有了事务处理能力

 public interface IOrderService : IScopeInstance
    {
        [UnitOfWork]
        void Add(OrderViewModel order);
        OrderViewResult Get(string sn);
    }

Asp.net Core 系列之--4.事务、日志及错误处理第25张

 程序运行时,Add方法前后形成切面,如下图所示,next(context)这里执行的就是Add方法,执行前开启事务,执行后提交

Asp.net Core 系列之--4.事务、日志及错误处理第26张

 利用Aop特性切面实现事务的无感注入(Asp.net Core 系列之--1.事件驱动初探:简单事件总线实现(SimpleEventBus)Ioc/DI小节中引入了AspectCore动态代理),底层还是依赖IDbConnection的事务相关接口,完整的事务处理大概就是这样了。

详细代码在Github的https://github.com/ChuanGoing/Start.git  的Domain分支可以找到。

    

免责声明:文章转载自《Asp.net Core 系列之--4.事务、日志及错误处理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Python解析Pcap包类源码学习H5的拖放下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MySql数据库优化、备份和恢复

一、数据库优化 1、为什么要优化 系统的吞吐量瓶颈往往出现在数据库的访问速度上 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢 数据是存放在磁盘上的,读写速度无法和内存相比 优化原则:减少系统瓶颈,减少资源占用,增加系统的反应速度。 2、数据库结构优化 需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容。 将...

Android 桌面组件【widget】初探

转自:http://www.cnblogs.com/TerryBlog/archive/2010/07/29/1788319.html 本来打算晚上继续 Api Demos 系列的,不过今天下午的时候无聊去玩了一下桌面组件 App Widget 觉得挺不错的一个东西,对它很是感兴趣,玩了一下碰到很多问题,一直在解决问题到了晚上10点。只能怪自己理解不深刻,...

java 调用apache.commons.codec的包简单实现MD5加密

转自:https://blog.csdn.net/mmd1234520/article/details/70210002/ 1 importjava.security.MessageDigest; 2 importjava.security.NoSuchAlgorithmException; 3 4 import org.apache....

android: 记录app运行过程中产生的log

有时在解决问题时,经常需要借助logcat才能分析定位问题,这里写了一个小工具,能够记录app运行期间的log, 这样测试人员在反馈bug时,只需要把logcat发给我们就可以了。具体代码如下: import android.content.Context; import android.content.Intent; import android.net...

基于python的种子搜索网站-开发过程

本讲会对种子搜索网站的开发过程进行详细的讲解。 源码地址:https://github.com/geeeeeeeek/bt 项目开发过程 项目简介 该项目是基于python的web类库django开发的一套web网站,做为本人的毕业设计。本人的研究方向是一项关于搜索的研究项目。在该项目中,笔者开发了一个简单版的搜索网站,实现了对数据库数据的检索和更新。 网...

JAVA实现AD验证

package service;import java.util.Hashtable; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.directory.DirContext; import javax.naming.directo...