zoukankan      html  css  js  c++  java
  • ASP.NET Core中Middleware的使用

    ASP.NET 5中Middleware的基本用法

    在ASP.NET 5里面引入了OWIN的概念,大致意思是将网站部署、服务器、中间组件以及应用分离开,这里提到的Middleware就是中间组件。

    这里引用asp.net网站的介绍图

    Middleware的作用有点类似于httpmodule,服务器接收到的请求都会传递到各个Middleware,多个Middleware形成一个处理管道。

    由于不针对于特定的请求,所以Middleware的执行范围是在Application之外,这种模式很适合处理日志记录,权限验证和事务处理,也可以理解为AOP的一种实现方式。

    在ASP.NET 5里面,ASP.NET Identity就提供了用于权限认证的Middleware,在Startup类里面调用UseXXX的方法就可以加入支持不同权限验证的Middleware,具体可以参考ASP.NET Identity的介绍。

    除了Middleware除了第三方组件提供的之外,我们还可以实现自定义的Middleware。

    在ASP.NET 5里面,我们自定义一个Middleware需要继承类OwinMiddleware,如下形式

    public class LoggerMiddleware : OwinMiddleware
    {
        private readonly ILog _logger;   public LoggerMiddleware(OwinMiddleware next, ILog logger) : base(next)
        {
            _logger = logger;
        }   public override async Task Invoke(IOwinContext context)
        {
            _logger.LogInfo("Middleware begin");
            await this.Next.Invoke(context);
            _logger.LogInfo("Middleware end");
        }
    }
    Middleware In ASP.NET 5

     构造方法传入了下一个Middleware的实例,用于在执行完当前Middleware之后,执行下一个Middleware。

    然后在Startup的Configuration方法里调用
    public class Startup
    {
       public void Configuration(IAppBuilder app)
       {
          app.Use<LoggerMiddleware>(new TraceLogger());
       }
    }
    以上是ASP.NET 5中Middleware的使用简述。
    ————————————————————————————————————————————————————————————————————


    ASP.NET Core的Middleware的用法
    而在最新版的ASP.NET Core中Middleware的使用方式有了变化,下面通过一步步来看如何在ASP.NET Core中创建自定义的Middleware,描述一下新版中如何是定义Middleware以及多个Middleware的调用
    1.创建一个空白的ASP.NET Core Web项目

    image

    image

    2.然后在项目的根目录创建一个Middlewares文件夹

    image

    3. 接下来将创建多个不同功能的Middleware

    a.创建输出内容的Middleware

     在没有使用任何Middleware时,这时候的项目能运行,运行之后只会输出Hello World。那是因为在Startup的Configure方法里,有一个默认的Run

        app.Run(async (context) =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });

     不管输入什么url路径,都只会返回Hello World,这也可以理解为一个Middleware,是一个兜底的处理。

     其实在ASP.NET Core里面,MVC的引入也是通过Middleware的形式实现的,在引入MVC的组件后,可以通过app.UseMvc()方法引入组件,这里不详述。

     回到即将创建的自定义的Middleware,在新版中创建自定义的Middleware,不需要继承OwinMiddleware类,也没有提供这类的定义。

     自定义的Middleware看起来跟普通的class没什么区别,下面在Middlewares文件夹新建一个ContentMiddleware类,然后编写代码如下

    using System.Text;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Http;
    
    namespace CustomMiddleware.Middlewares
    {
        public class ContentMiddleware
        {
            private RequestDelegate _nextDelegate;
    
            public ContentMiddleware(RequestDelegate nextDelegate)
            {
                _nextDelegate = nextDelegate;
            }
    
            public async Task Invoke(HttpContext httpContext)
            {
                if (httpContext.Request.Path.ToString().ToLower() == "/middleware")
                {
                    await httpContext.Response.WriteAsync(
                        "Handled by content middleware", Encoding.UTF8);
                }
                else
                {
                    await _nextDelegate.Invoke(httpContext);
                }
            }
        }
    }
    ContentMiddleware

    从这个类我们看到,没有继承任何类或者实现任何接口,但是方法的签名类似于之前版本的实现。

    在.NET Core版本中,自定义Middleware提供一个包含RequestDelegate 类型参数的构造方法,另外提供如下格式的Invoke方法签名即可

    public async Task Invoke(HttpContext httpContext)。

    其中RequestDelegate实例表示的是下一个执行的Middleware,这点跟OwinMiddleware类似。

    Invoke方法接收的是HttpContext类似的参数,相比之前OwinConext,感觉这样更加直接。

    这个ContentMiddleware的实现是,判断请求的路径如果是/middleware,就返回内容,否则交给下一个组件处理。

    定义完成之后,在Startup的Configure方法中,在app.Run()代码之前加入下面的代码

    app.UseMiddleware<ContentMiddleware>();

    运行之后输入http://localhost:53814/middleware就会看到浏览器输出了以下内容

    ”Handled by content middleware“

     如果输入其他url还是会显示”Hello world”。

     

    b.创建可终止后续请求的Middleware

    刚才提供Middleware的作用可用于校验请求内容,然后根据请求信息作出是否继续处理请求的操作。下面我们创建一个Middleware,判断如果是IE浏览器就返回403状态码,那么后续的请求将不被执行。

    在Middlewares文件夹创建ValidateBrowserMiddleware类,填入代码如下

    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Http;
    
    namespace CustomMiddleware.Middlewares
    {
        public class ValidateBrowserMiddleware
        {
            private readonly RequestDelegate _nextDelegate;
    
            public ValidateBrowserMiddleware(RequestDelegate nextDelegate)
            {
                _nextDelegate = nextDelegate;
            }
    
            public async Task Invoke(HttpContext httpContext)
            {
                if (httpContext.Request.Headers["User-Agent"]
                    .Any(h => h.ToLower().Contains("trident")))
                {
                    httpContext.Response.StatusCode = 403;
                }
                else
                {
                    await _nextDelegate.Invoke(httpContext);
                }
            }
        }
    }
    ValidateBrowserMiddleware

    同样在Startup的Configure方法加入引用,把这个组件放入到其他Middleware引用之前。

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
            {
                loggerFactory.AddConsole();
    
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseMiddleware<ValidateBrowserMiddleware>();
                app.UseMiddleware<ContentMiddleware>();
                app.Run(async (context) =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });
            }
    Configure

    Middleware的引用顺序很关键,最先引用的最先处理Request,并且是最后处理Response,是一种后进先出的处理顺序。

    这时候运行网站,如果是在IE浏览器下访问,那么会返回403的错误信息(禁止访问),后续的请求都不会执行,提早中断了请求。

     

    c.修改请求上下文的Middleware

    这里说的上下文是Httpcontext,这里有一个Items属性,可以存放一些数据用于当前的请求。

    在Middlewares文件夹创建EditContextMiddleware,修改代码如下

    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Http;
    
    namespace CustomMiddleware.Middlewares
    {
        public class EditContextMiddleware
        {
            private readonly RequestDelegate _nextDelegate;
    
            public EditContextMiddleware(RequestDelegate nextDelegate)
            {
                _nextDelegate = nextDelegate;
            }
    
            public async Task Invoke(HttpContext httpContext)
            {
                httpContext.Items["IEBrowser"] = httpContext.Request.Headers["User-Agent"]
                    .Any(v => v.ToLower().Contains("trident"));
                await _nextDelegate.Invoke(httpContext);
            }
        }
    }
    EditContextMiddleware

    这个Middleware在httpContext中加入了一个是否IEBrowser的数据项,然后接着执行后续的组件,也就是说后续的组件可以在httpContext中获取这个值。

    基于这个改动我们可以修改ValidateBrowserMiddleware的Invoke方法的实现

    public async Task Invoke(HttpContext httpContext)
            {
                var isIEBrowser = httpContext.Items["IEBrowser"] as bool?;
                if (isIEBrowser.GetValueOrDefault())
                {
                    httpContext.Response.StatusCode = 403;
                }
                else
                {
                    await _nextDelegate.Invoke(httpContext);
                }
            }

    这个改动的一个前提是EditContextMiddleware的引入必须在ValidateBrowserMiddleware之前,如下

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
            {
                loggerFactory.AddConsole();
    
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseMiddleware<EditContextMiddleware>();
                app.UseMiddleware<ValidateBrowserMiddleware>();
                app.UseMiddleware<ContentMiddleware>();
                app.Run(async (context) =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });
            }
    Configure

    d.最后一个是修改Repsonse的Middleware

    之前的都是处理Request或者修改HttpContext内容,根据上一个Middleware的处理结果来修改Response也是常见的做法。

    在Middlewares文件夹创建类StatusCodeMiddleware,代码内容如下

    public class StatusCodeMiddleware
        {
            private readonly RequestDelegate _requestDelegate;
    
            public StatusCodeMiddleware(RequestDelegate requestDelegate)
            {
                _requestDelegate = requestDelegate;
            }
    
            public async Task Invoke(HttpContext httpContext)
            {
                await _requestDelegate.Invoke(httpContext);
    
                switch (httpContext.Response.StatusCode)
                {
                    case 403:
                        httpContext.Response.StatusCode = 200;
                        await httpContext.Response.WriteAsync("IE not supported", Encoding.UTF8);
                        break;
                    case 404:
                        await httpContext.Response.WriteAsync("No page found", Encoding.UTF8);
                        break;
                }
            }
        }
    StatusCodeMiddleware

    上述代码的根据不同的Http状态码返回Response输出,如果是IE访问那么会显示一段文字,而不是一个403错误页。

    由于处理的是Repsonse的内容,因此是在Invoke方法之后执行,并且这个Middleware必须放在第一位,这样才能确保这Middleware处理的是最后一个Repsonse,因为Repsonse的处理顺序跟Request的处理顺序是相反的。

    StatusCodeMiddleware的引用代码:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
            {
                loggerFactory.AddConsole();
    
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseMiddleware<StatusCodeMiddleware>();
                app.UseMiddleware<EditContextMiddleware>();
                app.UseMiddleware<ValidateBrowserMiddleware>();
                app.UseMiddleware<ContentMiddleware>();
                app.Run(async (context) =>
                {
                    await context.Response.WriteAsync("Hello World!");
                });
            }
    Configure

    至此,已经使用了4个自定义的Middleware,对于一个请求每个middleware的处理顺序如下

    image_thumb8

    以上是ASP.NET Core中使用自定义Middleware的基本用法,基于这个实现我们做更多有意义的事情,比如日志记录、事务处理等。

    上述例子的代码在如下路径

    https://github.com/shenba2014/AspDotNetCoreMvcExamples/tree/master/CustomMiddleware

  • 相关阅读:
    记一次bash脚本报错原因
    说说JSON和JSONP,也许你会豁然开朗,含jQuery用例(转载)
    python 正则空格xa0实录 与xpath取 div 里面的含多个标签的所有文字
    python3的时间日期处理
    easyui的 一些经验
    hash是什么?
    vue.js 入门
    python __nonzero__方法
    Jmeter之『并发』
    Docker之网络篇
  • 原文地址:https://www.cnblogs.com/shenba/p/6361311.html
Copyright © 2011-2022 走看看