ASP.NET Core搭建多层网站架构【9.2-使用Castle.Core实现动态代理拦截器】

ASP.NET Core搭建多层网站架构【9.2-使用Castle.Core实现动态代理拦截器】

2020/01/31, ASP.NET Core 3.1, VS2019, Autofac.Extras.DynamicProxy 4.5.0, Castle.Core.AsyncInterceptor 1.7.0

摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构【9.2-使用Castle.Core实现动态代理拦截器】
介绍了如何对业务层方法进行拦截,捕获业务方法发生的错误,然后统一进行日志记录,避免在每个业务方法中进行try catch捕获异常

文章目录

此分支项目代码

本章节介绍了如何对业务层方法进行拦截,捕获业务方法发生的错误,然后统一进行日志记录,避免在每个业务方法中进行try catch捕获异常。借助Autofac和Castle.Core实现动态代理拦截器,其中使用Castle.Core.AsyncInterceptor包实现异步拦截器。

添加包引用#

MS.Component.Aop类库中添加以下包引用:

<ItemGroup>
  <PackageReference Include="Autofac.Extras.DynamicProxy" Version="4.5.0" />
  <PackageReference Include="Castle.Core.AsyncInterceptor" Version="1.7.0" />
</ItemGroup>
  • Autofac.Extras.DynamicProxy包和Autofac.Extensions.DependencyInjection包中的autofac版本最好一致,否则可能会出现代理拦截器注册失败的情况
  • 可以先按我的包版本使用,确保拦截器无问题,再把包更新到最新版
  • Autofac.Extras.DynamicProxy包是使用Autofac和Castle.Core实现动态代理拦截器
  • Castle.Core.AsyncInterceptor包是实现异步拦截器(否则只能对同步方法进行拦截)
  • MS.Component.Aop类库要依赖MS.WebCore类库。

编写服务拦截器#

MS.Component.Aop类库中添加LogAop文件夹,在该文件夹下新建AopHandledException.csLogInterceptor.csLogInterceptorAsync.cs

AopHandledException.cs#

using System;

namespace MS.Component.Aop
{
    /// <summary>
    /// 使用自定义的Exception,用于在aop中已经处理过的异常,在其他地方不用重复记录日志
    /// </summary>
    public class AopHandledException : ApplicationException
    {
        public string ErrorMessage { get; private set; }
        public Exception InnerHandledException { get; private set; }
        //无参数构造函数
        public AopHandledException()
        {

        }
        //带一个字符串参数的构造函数,作用:当程序员用Exception类获取异常信息而非 MyException时把自定义异常信息传递过去
        public AopHandledException(string msg) : base(msg)
        {
            this.ErrorMessage = msg;
        }
        //带有一个字符串参数和一个内部异常信息参数的构造函数
        public AopHandledException(string msg, Exception innerException) : base(msg)
        {
            this.InnerHandledException = innerException;
            this.ErrorMessage = msg;
        }
        public string GetError()
        {
            return ErrorMessage;
        }
    }
}

这里自定义了一个AopHandledException异常类型,目的是为了:

  • 拦截器捕获异常后已经进行了日志记录,这个异常可能需要继续抛出去,此时用AopHandledException异常类型包一层异常,这样后面再捕获到的异常就能判断是从Aop中抛出来的异常了,不再重复记录日志

LogInterceptor.cs#

using Castle.DynamicProxy;

namespace MS.Component.Aop
{
    public class LogInterceptor : IInterceptor
    {
        private readonly LogInterceptorAsync _logInterceptorAsync;

        public LogInterceptor(LogInterceptorAsync logInterceptorAsync)
        {
            _logInterceptorAsync = logInterceptorAsync;
        }

        public void Intercept(IInvocation invocation)
        {
            _logInterceptorAsync.ToInterceptor().Intercept(invocation);
        }
    }
}

这个是主拦截器,继承自IInterceptor,不管是同步方法还是异步方法,都将会走其中的Intercept方法,然后会在LogInterceptorAsync中再去区分是异步方法还是同步方法

LogInterceptorAsync.cs#

using Castle.DynamicProxy;
using Microsoft.Extensions.Logging;
using MS.Common.Extensions;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace MS.Component.Aop
{
    public class LogInterceptorAsync : IAsyncInterceptor
    {
        private readonly ILogger<LogInterceptorAsync> _logger;

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

        /// <summary>
        /// 同步方法拦截时使用
        /// </summary>
        /// <param name="invocation"></param>
        public void InterceptSynchronous(IInvocation invocation)
        {
            try
            {
                //调用业务方法
                invocation.Proceed();
                LogExecuteInfo(invocation, invocation.ReturnValue.ToJsonString());//记录日志
            }
            catch (Exception ex)
            {
                LogExecuteError(ex, invocation);
                throw new AopHandledException();
            }
        }

        /// <summary>
        /// 异步方法返回Task时使用
        /// </summary>
        /// <param name="invocation"></param>
        public void InterceptAsynchronous(IInvocation invocation)
        {
            try
            {
                //调用业务方法
                invocation.Proceed();
                LogExecuteInfo(invocation, invocation.ReturnValue.ToJsonString());//记录日志
            }
            catch (Exception ex)
            {
                LogExecuteError(ex, invocation);
                throw new AopHandledException();
            }
        }

        /// <summary>
        /// 异步方法返回Task<T>时使用
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="invocation"></param>
        public void InterceptAsynchronous<TResult>(IInvocation invocation)
        {
            //调用业务方法
            invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
        }
        private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
        {
            try
            {
                //调用业务方法
                invocation.Proceed();
                Task<TResult> task = (Task<TResult>)invocation.ReturnValue;
                TResult result = await task;//获得返回结果
                LogExecuteInfo(invocation, result.ToJsonString());

                return result;
            }
            catch (Exception ex)
            {
                LogExecuteError(ex, invocation);
                throw new AopHandledException();
            }
        }

        #region helpMethod
        /// <summary>
        /// 获取拦截方法信息(类名、方法名、参数)
        /// </summary>
        /// <param name="invocation"></param>
        /// <returns></returns>
        private string GetMethodInfo(IInvocation invocation)
        {
            //方法类名
            string className = invocation.Method.DeclaringType.Name;
            //方法名
            string methodName = invocation.Method.Name;
            //参数
            string args = string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray());


            if (string.IsNullOrWhiteSpace(args))
            {
                return $"{className}.{methodName}";
            }
            else
            {
                return $"{className}.{methodName}:{args}";
            }
        }
        private void LogExecuteInfo(IInvocation invocation, string result)
        {
            _logger.LogDebug("方法{0},返回值{1}", GetMethodInfo(invocation), result);
        }
        private void LogExecuteError(Exception ex, IInvocation invocation)
        {
            _logger.LogError(ex, "执行{0}时发生错误!", GetMethodInfo(invocation));
        }
        #endregion
    }
}

这里是对方法拦截的主要实现:

  • InterceptSynchronous是同步方法拦截时使用
  • InterceptAsynchronous是异步方法返回Task时使用
  • InterceptAsynchronous是异步方法返回Task(包含返回值)时使用
  • invocation.Proceed();这句话即是调用真正的业务方法,所以在该方法之前可以做一些权限判断的内容,在该方法之后可以获取记录业务返回结果
  • 可以在invocation参数中获取方法名称、传递的参数等信息
  • 如果做了用户登录,可以通过构造函数依赖注入IHttpContextAccessor以拿到执行业务者的信息(其实使用NLog记录日志时,NLog也能获取到登录用户的信息,就是NetUserIdentity这个参数)
  • 在拦截器中,我对每个业务方法都以Debug的日志等级记录了调用业务方法的返回结果

至此对业务方法进行拦截,以Debug的日志等级记录了调用业务方法的返回结果,并捕获所有业务方法的异常已经完成。

封装Ioc注册#

MS.Component.Aop类库中添加AopServiceExtensions.cs类:

using Autofac;
using Autofac.Extras.DynamicProxy;
using System;
using System.Reflection;

namespace MS.Component.Aop
{
    public static class AopServiceExtension
    {
        /// <summary>
        /// 注册aop服务拦截器
        /// 同时注册了各业务层接口与实现
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="serviceAssemblyName">业务层程序集名称</param>
        public static void AddAopService(this ContainerBuilder builder, string serviceAssemblyName)
        {
            //注册拦截器,同步异步都要
            builder.RegisterType<LogInterceptor>().AsSelf();
            builder.RegisterType<LogInterceptorAsync>().AsSelf();

            //注册业务层,同时对业务层的方法进行拦截
            builder.RegisterAssemblyTypes(Assembly.Load(serviceAssemblyName))
                .AsImplementedInterfaces().InstancePerLifetimeScope()
                .EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
                .InterceptedBy(new Type[] { typeof(LogInterceptor) })//这里只有同步的,因为异步方法拦截器还是先走同步拦截器 
                ;

            //业务层注册拦截器也可以使用[Intercept(typeof(LogInterceptor))]加在类上,但是上面的方法比较好,没有侵入性
        }
    }
}

说明:

  • 在AddAopService方法中,传递了业务层程序集名称,使用Autofac统一注册业务层所有的接口和实现(这样就不用每个接口都写一次注册了),注册类型为InstancePerLifetimeScope
  • 注册业务的同时,开启了代理拦截器:EnableInterfaceInterceptors
  • 开启代理拦截器的同时,InterceptedBy传递了要使用哪些拦截器进行拦截,这里只有同步的,因为异步方法拦截器还是先走同步拦截器
  • 业务层注册拦截器也可以使用[Intercept(typeof(LogInterceptor))]加在类上,但是上面的方法比较好,没有侵入性

获取业务层程序集名称#

MS.Services中添加ServiceExtensions.cs类:

using System.Reflection;

namespace MS.Services
{
    public static class ServiceExtensions
    {
        /// <summary>
        /// 获取程序集名称
        /// </summary>
        /// <returns></returns>
        public static string GetAssemblyName()
        {
            return Assembly.GetExecutingAssembly().GetName().Name;
        }
    }
}

用于获取业务层的程序集名称,提供给Autofac进行批量的注册接口和实现。

注册Aop服务#

MS.WebApi应用程序中,Startup.cs:
在ConfigureContainer方法里删掉原先对IBaseService、IRoleService的接口注册,使用刚写的Aop注册,给业务层批量注册同时开启代理拦截:

//using MS.Services;
//以上代码添加至using
//注册aop拦截器 
//将业务层程序集名称传了进去,给业务层接口和实现做了注册,也给业务层各方法开启了代理
builder.AddAopService(ServiceExtensions.GetAssemblyName());

完成后,代码如下图所示

至此,业务层方法的代理拦截都完成了,执行业务时,会在控制台显示每次业务的调用返回结果,如果遇到异常,会统一捕获并记录日志然后把异常包装一次再抛出去

启动项目,打开Postman测试接口:

可以看到返回结果显示出来了,可以打断点看看程序到底怎么通过拦截器的。

这里只是实现了业务层方法拦截,异常日志的捕获,可以做更多例如权限验证的拦截器。

项目完成后,如下图所示

作者:kasnti

出处:https://www.cnblogs.com/kasnti/p/12244544.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要将ASP.NET Core Angular项目部署到IIS上,并且遇到"non-js module files deprecated"的问题。 首先,我们需要确保我们正在使用的是最新版本的ASP.NET Core和Angular。新版本通常会修复旧版中的已知问题。 然后,我们需要检查我们的Angular项目中是否有使用到非JS模块文件(non-js module files)。这些文件可能是早期版本中的遗留文件,因此被标记为过时(deprecated)。 要解决这个问题,我们可以按照以下步骤进行操作: 1. 检查Angular项目中的Angular.json配置文件。在该文件中,我们需要确保所有的非JS模块文件都被正确地排除在构建过程之外。这可以通过在 "build" 部分中的 "assets" 属性中排除这些文件来实现。 2. 确保我们使用的是正确的构建命令。在命令行或脚本中,我们应该使用 "ng build" 命令来构建我们的Angular项目,并确保在构建过程中自动排除非JS模块文件。例如,我们可以使用以下命令构建项目: ``` ng build --prod ``` 3. 删除任何已经存在的非JS模块文件,以避免将其错误地部署到服务器上。通常,这些文件可以在Angular项目的 "src" 目录中找到,并且可能以 ".css", ".html" 或 ".json" 结尾。 4. 部署我们的ASP.NET Core Angular项目到IIS。可以通过将项目文件复制到IIS网站目录中或使用自动化工具(例如Web Deploy)进行部署来实现。确保将ASP.NET Core应用程序部署为网站的子目录,以便正确配置IIS的应用程序池。 通过按照上述步骤进行操作,我们应该能够成功地将ASP.NET Core Angular项目部署到IIS上并解决"non-js module files deprecated"的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值