首页 > 其他分享 >.NET 过滤器、中间件、AOP、拦截器及其应用

.NET 过滤器、中间件、AOP、拦截器及其应用

时间:2023-11-15 13:48:31浏览次数:37  
标签:拦截器 中间件 Filter context AOP 过滤器 public

一、过滤器(Filter)

        一共5种、 Authorization Filter,Resource Filter,Exception Filter,Action Filter,Result Filter

       1.1、Exception Filter

                新增全局异常过滤器GlobalExceptionFilter.cs。

                当出现异常时进入此方法,可在这针对不同的异常做相关处理并返回指定数据,避免直接把错误暴露给用户。              

public class GlobalExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        Exception ex = context.Exception;
        string errMsg = "GlobalExceptionFilter-OnException:" +                 ex.Message;
        if (context.Exception.GetType() == typeof(ExecuteException))
     {
        //针对不同的自定义异常,做不同处理
        MsgModel<string> msgModel = new MsgModel<string>()
        {
            Status = false,
            Msg = errMsg,
            Errcode = "AA001"
        };
        context.Result = new JsonResult(msgModel);
        context.ExceptionHandled = true;
    }
    else
    {
      context.Result = new JsonResult(errMsg);
      context.ExceptionHandled = true;
    }

    LogHelper.Error(errMsg);
  }
}
View Code

                然后在Startup注册:

        1.2、Action Filte

                新增全局过滤器GlobalActionFilter.cs 

                在方法执行前后,会跳转至以下两个方法,方便追踪接口执行情况 

                过去返回的参数:              

var result = context.Result;
            ObjectResult objectResult= result as ObjectResult; 
            var resultLog = $"{DateTime.Now} 调用 {context.RouteData.Values["action"]} api 完成;执行结果:{Newtonsoft.Json.JsonConvert.SerializeObject(objectResult.Value)}"; 
View Code

                获取请求的参数:

var actionLog = $"{DateTime.Now} 开始调用 {context.RouteData.Values["action"]} api;参数为:{Newtonsoft.Json.JsonConvert.SerializeObject(context.ActionArguments)}";
View Code
public class GlobalActionFilter : IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
    //LogHelper.Info("OnActionExecuted");
    //执行方法后执行这
   }

  public void OnActionExecuting(ActionExecutingContext context)
  {
  //LogHelper.Info("OnActionExecuting");
  //执行方法前先执行这
  }
}
View Code

         1.3、Authonization Filter

                权限控制过滤器

                通过 Authonization Filter 可以实现复杂的权限角色认证登陆授权等操作

/// <summary>
    /// 实现自定义授权
    /// </summary>
    public class AuthorizeFilter : IAuthorizationFilter
    {
        /// <summary>
        /// 请求验证,当前验证部分不要抛出异常,ExceptionFilter不会处理
        /// </summary>
        /// <param name="context"></param>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
         //这里可以做复杂的权限控制操作                    //if (context.HttpContext.User.Identity.Name != "1") //简单的做一个示范                    //{                    //    //未通过验证则跳转到无权限提示页                    //    RedirectToActionResult content = new RedirectToActionResult("NoAuth", "Exception", null);                    //    context.Result = content;                    //

        }
    }
View Code

          1.4、Resource Filter

                资源过滤器

                可以通过Resource Filter 进行资源缓存防盗链等操作

                使用Resource Filter 要求实现IResourceFilter 抽象接口

public class ResourceFilter : Attribute,IResourceFilter
    {
        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            // 执行完后的操作
        }

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            // 执行中的过滤器管道
        }
    }
View Code

          1.5、Result Filter

                 结果过滤器,可以对结果进行格式化、大小写转换等一系列操作。

      使用Result Filter 需要实现IResultFilter 抽象接口,接口要求实现
      OnResultExecuting 方法 和OnResultExecuted 方法

      OnResultExecuting 方法:Called before the action result executes. 在操作结果执行之前调用

      OnResultExecuted   方法:Called after the action result executes. 在操作结果执行之后调用

public class ResultFilter : Attribute, IResultFilter
 {
        public void OnResultExecuted(ResultExecutedContext context)
        { 
            // 在结果执行之后调用的操作...
        }

        public void OnResultExecuting(ResultExecutingContext context)
        {
            // 在结果执行之前调用的一系列操作
        }
    }
View Code

         完毕 可以在全局注入

 二、中间件

        中间件是一种装配到应用管道以处理请求和响应的软件。 每个组件:

        1、选择是否将请求传递到管道中的下一个组件;

        2、可在管道中的下一个组件前后执行工作。

        请求委托用于生成请求管道。 请求委托处理每个 HTTP 请求。

/// <summary>
    /// 中间件
    /// 记录请求和响应数据
    /// </summary>
    public class RequestMiddleware
    {

        private readonly RequestDelegate _next;

        /// <summary>
        /// 日志接口
        /// </summary>
        private static Logger logger = LogManager.GetCurrentClassLogger();

        private Stopwatch _stopwatch;

        public RequestMiddleware(RequestDelegate next)
        {
            _stopwatch = new Stopwatch();
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // 过滤,只有接口
            if (context.Request.Path.Value.ToLower().Contains("api"))
            {
                context.Request.EnableBuffering();
                Stream originalBody = context.Response.Body;

                _stopwatch.Restart();

                // 获取 Api 请求内容
                var requestContent = await GetRequesContent(context);


                // 获取 Api 返回内容
                using (var ms = new MemoryStream())
                {
                    context.Response.Body = ms;

                    await _next(context);
                    ms.Position = 0;

                    await ms.CopyToAsync(originalBody);
                }

                context.Response.Body = originalBody;

                _stopwatch.Stop();

                var eventInfo = new LogEventInfo();
                eventInfo.Message = "Success";
                eventInfo.Properties["Elapsed"] = _stopwatch.ElapsedMilliseconds;
                eventInfo.Properties["RequestBody"] = requestContent;

                logger.Trace(eventInfo);
            }
            else
            {
                await _next(context);
            }
        }

        private async Task<string> GetRequesContent(HttpContext context)
        {
            var request = context.Request;
            var sr = new StreamReader(request.Body);

            var content = $"{await sr.ReadToEndAsync()}";

            if (!string.IsNullOrEmpty(content))
            {
                request.Body.Position = 0;
            }

            return content;
        }
    }
View Code

       然后在Startup注册

// 请求日志监控
app.UseMiddleware<RequestMiddleware>();
View Code

三、AOP

       一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。     

          AOP是是OOP(面向对象)的补充和完善

          拦截器是在面向切面编程中应用的,就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法。

          AOP说白了就是在运行时,动态的将代码切入到类的指定方法的指定位置上,这种思想就是面向切面的编程思想。

         就是 在不修改源代码的基础上 添加新业务,比如 日志 性能检测。

         从思想上来说,Filter(过滤器)跟AOP(拦截器)极其接近。

但是区别明显:
1、Filter只能拦截request的请求;

2、Spring中的AOP,一般而言,是在Service层,拦截Bean(实例)的访问。

例如使用@Transcational 来拦截dao的应用,让它实现事务的管理。从思想上来说,Filter跟AOP极其接近

       1、拦截器是基于java的反射机制,过滤器是基于java的函数回调

  2、拦截器不依赖于servlet容器,而过滤器依赖于servlet容器

  3、拦截器只能对action请求起作用,过滤器几乎对所有的请求起作用

  4、拦截器可以访问action上下文,值栈里的对象,而过滤器不能访问

  5、在action生命周期中,拦截器可以被多次调用,过滤器只能在servlet初始化时调用一次

  6、拦截器可以获取IOC容器中的各个bean,过滤器不行,在拦截器中注入一个service可以调用逻辑业务

       Aop 其实就是动态代理

四、拦截器(Interceptor)

        4.1、概念

        java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action
执行前阻止其执行,同时也提供了一种可以提取Action中可重用部分代码的方式。在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截
然后再之前或者之后加入某些操作。目前,我们需要掌握的主要是Spring的拦截器,Struts2的拦截器不用深究,知道即可。

       4.2、原理

       大部分时候,拦截器方法都是通过代理的方式来调用的。Struts2的拦截器实现相对简单。当请求到达Struts2的ServletDispatcher时,Struts2
会查找配置文件,并根据配置实例化相对的拦截器对象,然后串成一个列表(List),最后一个一个的调用列表中的拦截器。Struts2的拦截器是可
插拔的,拦截器是AOP的一个实现。Struts2拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截的方法或者字段时,Struts2拦截器链
中的拦截器就会按照之前定义的顺序进行调用。

      4.3、自定义拦截器的步骤

第一步:自定义一个实现了Interceptor接口的类,或者继承抽象类AbstractInterceptor。
第二步:在配置文件中注册定义的拦截器。
第三步:在需要使用Action中引用上述定义的拦截器,为了方便也可以将拦截器定义为默认的拦截器,这样在不加特殊说明的情况下,所有的Action都被这个拦截器拦截。

     4.4、过滤器与拦截器的区别

         过滤器可以简单的理解为“取你所想取”,过滤器关注的是web请求;拦截器可以简单的理解为“拒你所想拒”,拦截器关注的是方法调用,比如拦截敏感词汇。
1,拦截器是基于java反射机制来实现的,而过滤器是基于函数回调来实现的。(有人说,拦截器是基于动态代理来实现的)
2,拦截器不依赖servlet容器,过滤器依赖于servlet容器。
3,拦截器只对Action起作用,过滤器可以对所有请求起作用。
4,拦截器可以访问Action上下文和值栈中的对象,过滤器不能。
5,在Action的生命周期中,拦截器可以多次调用,而过滤器只能在容器初始化时调用一次。

    4.5、例子

            LogInterceptor

            安装Castle.Core,Autofac.Extras.DynamicProxy

            新建LogInterceptor.cs ,继承IInterceptor    

public class LogInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            try
            {
                invocation.Proceed();
                Dapper.Logger.LogHelper.logger.Info(invocation.Method.Name);
            }
            catch (Exception ex)
            {
                Dapper.Logger.LogHelper.logger.Error(invocation.Method.Name + " " + ex.ToString());
            }
        }
    }
View Code

            在Starup中,新增以下代码:

     4.6、针对某个类或者某个方法做拦截时

              新建一个拦截器 MyInterceptor

public class MyInterceptor : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            try
            {
                invocation.Proceed();
                NLogHelper.logger.Info(invocation.Method.Name);
            }
            catch (Exception ex)
            {
                NLogHelper.logger.Error(invocation.Method.Name + " " + ex.ToString());
            }
        }
    }
View Code

然后Startup.cs 中ConfigureContainer代码如下

把LogInterceptor 代码注释,但是要保留接口拦截EnableInterfaceInterceptors() ,注入MyInterceptor

public void ConfigureContainer(ContainerBuilder builder)
    {
        //builder.RegisterType<LogInterceptor>();

        builder.RegisterType<MyInterceptor>();

        builder.RegisterType<DbFactory>().As<IDbFactory>();

        //业务逻辑层所在程序集命名空间
        Assembly service = Assembly.Load("Summer.Service"); //注:webapi要引用接口和类,不然这里读不到
                                                            //接口层所在程序集命名空间
        Assembly repository = Assembly.Load("Summer.IService");
        //自动注入
        builder.RegisterAssemblyTypes(service, repository)
        .Where(t => t.Name.EndsWith("Service"))
        .AsImplementedInterfaces()
        .InstancePerLifetimeScope()
        .EnableInterfaceInterceptors() //开启接口拦截
                                       //.InterceptedBy(typeof(LogInterceptor)) //设置全局拦截器,统一由LogInterceptor拦截所有接口的调用
        ;
    }
View Code

               然后在需要拦截的接口中添加以下代码

           拦截器设置完毕,当调用ITestService 的全部方法都会跳转拦截器

           以下代码是一个示例,利用反射查看权限

public class AuthInterceptor : IInterceptor
    {

        public void Intercept(IInvocation invocation)
        {

            Type tt = invocation.InvocationTarget.GetType();
            DoMain.Model.SYS.SYS_User userinfo = null;

            var p = tt.GetProperties().FirstOrDefault(q => q.Name == "UserInfo");

            userinfo = (DoMain.Model.SYS.SYS_User)p.GetValue(invocation.InvocationTarget);
            var authkey = invocation.TargetType.ToString() + "." + invocation.Method.Name;

            if (AuthChecker.isAuth(userinfo, authkey))
            {
                invocation.Proceed();
            }
            else
            {
                throw new Core.AuthException("没有操作权限");
            }

            //方法执行后的操作


        }
View Code

         Filter和 LogInterceptor 可以同时共存,执行顺序是:

ActionFilter 的OnActionExecuting =》LogInterceptor 的Intercept =》ActionFilter 的OnActionExecuted 

        如果接口有异常,不会跳转LogInterceptor ,而是进入ExceptionFilter,顺序是:

ActionFilter 的OnActionExecuting =》ActionFilter 的OnActionExecuted =》ExceptionFilter 的OnException

五、AOP的应用

        5.1、引用三个包,通过Nuget安装,Autofac开头,如下:               

                 

                 注: 其中Autofac.Extras.DynamicProxy就是AOP相关组件,其中包含了Castle.Core,所以不用单独安装Castle.Core

        5.2、模拟编写用户维护相关逻辑,代码如下:

                接口:             

                

                实现:

               

        5.3、编写拦截器逻辑,即代理:

              

        5.4、集成Autofac将用户维护服务这块进行注册到容器中

               首先在Startup中增加方法,如下:

               

        5.5、然后在program中添加Autofac的工厂,如下:

                   

      5.6、增加UserController方法,如下:

                

 5.7、运行测试,为了方便看见控制台打印,用项目启动方式进行运行,结果如下:

         直接在浏览器中输入http://localhost:5000/api/User/AddUser?name=sss&age=12,然后回车,然后看控制台打印:        

          

5.8、总结

         AOP在做一些业务前置或后置处理上时很有用的,使用比较灵活,无需修改原有代码逻辑,比起修改原有代码维护相对好多啦。

 

标签:拦截器,中间件,Filter,context,AOP,过滤器,public
From: https://www.cnblogs.com/xiaobaicai12138/p/17833444.html

相关文章

  • token以及axios响应拦截器请求拦截器
    一、token的介绍1.概念访问权限的令牌,本质上是一串字符串2.创建正确登录后,由后端签发并返回3.作用判断是否有登录状态等,控制访问权限注意:前端只能判断有无token,而后端才能判断token的有效性4.使用目标:只有登录状态,才能访问内容页面1.在utils/auth.js中判断有无token令牌字符串,则强......
  • 常见面试题-Spring的aop和ioc如何实现?
    Spring的aop和ioc怎么实现?Spring的IOC是如何实现的呢?Spring的IOC是通过工厂+反射去实现的,在IOC中,Spring会将创建的Bean都放在工厂中,我们可以通过@Configuration来定义配置类,在配置类中通过@Bean来将Bean创建在Bean工厂中,在对Bean进行实例化时,使用的......
  • 使用JWT、拦截器与ThreadLocal实现在任意位置获取Token中的信息,并结合自定义注解实现
    1.简介1.1JWTJWT,即JSONWebToken,是一种用于在网络上传递声明的开放标准(RFC7519)。JWT可以在用户和服务器之间传递安全可靠的信息,通常用于身份验证和信息交换。声明(Claims):JWT包含一组称为声明的信息,声明描述了一些数据。有三种类型的声明:注册声明(RegisteredClaims):这是......
  • SpringBoot定义拦截器+自定义注解+Redis实现接口防刷(限流)
    实现思路在拦截器Interceptor中拦截请求通过地址+请求uri作为调用者访问接口的区分在Redis中进行计数达到限流目的简单实现定义参数访问周期最大访问次数禁用时长#接口防刷配置,时间单位都是秒.如果second秒内访问次数达到times,就禁用lockTime秒access:lim......
  • AOP以注解为切入点,获取注解参数和切点方法参数名
    AOP以注解为切入点,获取注解参数和切点方法参数名importcn.lettin.base.response.ResponseObjBaseVo;importcn.lettin.base.response.ResponseVo;importcn.lettin.keeper.edge.utils.UserNodeAuthCheckUtils;importorg.aspectj.lang.ProceedingJoinPoint;importorg.asp......
  • 拦截器
    packagecom.comen.interceptor;importcom.comen.edata.bean.User;importcom.comen.edata.tools.JwtUtil;importorg.springframework.web.servlet.HandlerInterceptor;importorg.springframework.web.servlet.ModelAndView;importjavax.servlet.http.HttpServletR......
  • 什么是中间件?
    中间件的定义中间件(英语:Middleware),又译中间件、中介层,是一类提供系统软件和应用软件之间连接、便于软件各部件之间的沟通的软件,应用软件可以借助中间件在不同的技术架构之间共享信息与资源。中间件位于客户机服务器的操作系统之上,管理着计算资源和网络通信。–维基百科个人理......
  • 12、SpringMVC之拦截器
    12.1、环境搭建创建名为spring_mvc_interceptor的新module,过程参考9.1节和9.5节12.1.1、页面请求示例<ath:href="@{/test/hello}">测试拦截器</a>12.1.2、控制器方法示例@RequestMapping("/test/hello")publicStringtestHello(){return"succe......
  • 【openfeign】OpenFeign的扩展、日志、超时时间、拦截器、客户端组件、压缩
    Feign的日志配置有时候我们遇到Bug,比如接口调用失败、参数没收到等问题,或者想看看调用性能,就需要配置Feign的日志了,以此让Feign把请求信息输出来。全局配置定义一个配置类,指定日志级别:packagecom.morris.user.config;importfeign.Logger;importorg.springframework.context.a......
  • SpringBoot AOP + Redis 延时双删功能实战
    一、业务场景在多线程并发情况下,假设有两个数据库修改请求,为保证数据库与redis的数据一致性,修改请求的实现中需要修改数据库后,级联修改Redis中的数据。请求一:A修改数据库数据B修改Redis数据请求二:C修改数据库数据D修改Redis数据并发情况下就会存在A—>C—>D—>B的情......