美文网首页.NET asp.netdotNET
ASP.NET Core基本原理(2)-中间件

ASP.NET Core基本原理(2)-中间件

作者: CharlieChu | 来源:发表于2016-12-02 15:40 被阅读185次

    ASP.NET Core基本原理(2)-中间件

    原文链接:Application Startup

    什么是中间件

    中间件是装配到应用管道中用来处理请求和响应的软件组件。管道中的每一个组件都可以选择是否将请求移交给下一个组件,并且可以在管道中调用下一个组件之前或者之后执行指定的操作。请求委托被用于构建请求管道。请求委托会处理每一个HTTP请求。

    请求委托通过在传递给Startup类中的Configure方法的IApplicationBuilder类型上使用Run,Map,Use扩展方法进行配置。一个单独的请求委托可以被指定为一个内嵌的匿名方法,或定义在一个可重用的类中。这些可重用的类就是中间件中间件组件。每个请求管道中的中间件负责调用管道中的下一个组件,或者在适当的时候将调用链短路。

    通过IApplicationBuilder创建中间件管道

    ASP.NET请求管道由一系列的请求委托所组成,一个接着一个被调用,如图所示,执行线程跟随着黑色箭头:

    每一个委托在下一个委托调用之前或之后都有机会去执行操作。任何委托都可以选择停止传递请求到下一个委托,而自己处理该请求。这就是请求管道的短路,这种设计可以避免一些不必要的工作。例如,一个授权(Authorization)中间件只有在请求通过身份验证之后才能调用下一个委托;否则它就会被短路并返回"Not Authorized"的响应。异常处理委托必须在管道的早期被调用,这样它们就可以捕获到发生在管道内更深层次的异常。

    打开默认的网站模板,Configure方法添加了如下中间件组件:

    1. 错误处理(针对开发环境和非开发环境)
    2. 静态文件服务器
    3. 身份验证
    4. MVC
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();
    
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }
    
        app.UseStaticFiles();
    
        app.UseIdentity();
    
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
    

    上述代码(在非开发环境),UseExceptionHandler是第一个被添加到管道的中间件,因此会捕获之后调用中出现的任何异常。

    Static File Module提供了无需授权检查的功能,由它服务的任何文件,包含在那些位于wwwroot文件夹中的文件都是可被公开访问的。如果你想让基于授权来提供这些文件:

    1. 将它们存放在wwwroot文件夹之外以及静态文件中间件可以访问的任何目录。
    2. 交给控制器方法,通过返回一个FileResult表示授权被应用的地方。

    被静态文件模块处理的请求会在管道中被短路。如果请求不是通过静态文件模块来处理,那么它会被传给Identity module来执行身份验证。如果请求未通过验证,则管道将被短路。否则,将会调用管道的最后一站-MVC框架。

    注:你添加中间件的顺序通常会影响它们对请求产生影响的顺序,然后在响应时会以相反的顺序。这对应用程序的安全、性能和功能都非常关键。在上面的代码中,Static File Module在管道的早期被调用,这样可以及时短路,避免了请求进行到不必要的组件中。身份验证中间件在任何需要身份验证的处理请求之前被添加进来。异常处理必须在其它中间件之前被注册以便捕获其它组件的异常。

    最简单的ASP.NET应用设置一个简单的请求委托来处理所有的请求。这样就不是一个真实的请求管道,通过调用一个简单的匿名函数来响应每一个HTTP请求。

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

    App.Run委托会终止管道,下面的盒子中,只有第一个委托会被运行。

    public void Configure(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello, World, Again!");
        });
    }
    

    将多个请求委托链接在一起,next参数表示管道内的下一个委托。你可以通过不调用next参数来终止(短路)管道。你通常可以在调用下一个委托之前和之后执行操作:

    public void ConfigureLogInline(IApplicationBuilder app, ILoggerFactory loggerfactory)
    {
        loggerfactory.AddConsole(minLevel: LogLevel.Information);
        var logger = loggerfactory.CreateLogger(_environment);
        app.Use(async (context, next) =>
        {
            logger.LogInformation("Handling request.");
            await next.Invoke();
            logger.LogInformation("Finished handling request.");
        });
    
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from " + _environment);
        });
    }
    

    当应用程序运行的环境设置成LogInline时,ConfigureLogInline方法会被调用。

    在上述例子中,调用await next.Invoke()将会调用下一个委托await context.Response.WriteAsync("Hello from " + _environment);。客户端会收到所期望的响应("Hello from LogInline")。

    Run、Map和Use

    你可以通过Run,MapUse来配置HTTP管道。Run方法将会短路管道(因为它不会调用next请求委托),因此Run方法应该只在管道结尾被调用。Run方法是一种惯例,有些中间件也会暴露它们自己的Run[Middleware]方法,你也必须在管道的末尾进行运行。下面两个中间件是相同的,其中一个使用Use版本的没有使用到next参数:

    public void ConfigureEnvironmentOne(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from " + _environment);
        });
    }
    
    public void ConfigureEnvironmentTwo(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            await context.Response.WriteAsync("Hello from " + _environment);
        });
    }
    

    Map*扩展被用于分支管道,当前的实现支持基于请求路径或使用谓词进行分支。Map扩展方法被用于匹配基于请求路径的请求委托。Map只接受一个路径和配置了一个单独中间件的管道的功能。在下面的例子中,任何基于/maptest基本路径的请求都会被HandleMapTest方法中所配置的管道所处理。

    private static void HandleMapTest(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test Successful");
        });
    }
    
    public void ConfigureMapping(IApplicationBuilder app)
    {
        app.Map("/maptest", HandleMapTest);
    
    }
    

    当使用了Map,每一个请求所匹配的路径段将从HttpRequest.Path中移除,并附加到HttpRequest.PathBase中。

    除了基于路径的映射外,MapWhen方法还支持基于谓词的中间件分支,允许以一种非常灵活的方式构造单独的管道。任何Func<HttpContext, bool>类型的谓词都可被用于将请求映射到一个新的管道分支。在下面的例子中,一个简单的谓词被用来检测字符变量branch是否存在:

    private static void HandleBranch(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Branch used.");
        });
    }
    
    public void ConfigureMapWhen(IApplicationBuilder app)
    {
        app.MapWhen(context => {
            return context.Request.Query.ContainsKey("branch");
        }, HandleBranch);
    
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from " + _environment);
        });
    }
    

    上述配置中,任何包含branch的查询字符串的请求将使用定义在HandleBranch方法中的管道(将得到"Branch used."的响应)。其它请求(不包含branch的查询字符串的请求)将被await context.Response.WriteAsync("Hello from " + _environment);所定义的委托所处理。

    内置中间件

    ASP.NET带来了下列中间件组件:

    中间件 �描述 � � � � � � � � � � � � � � � � �
    身份验证(Authentication) �提供身份验证支持
    跨域资源共享(CORS) �配置跨域资源共享
    路由(Routing) �定义和约定请求路由
    会话(Session) �对管理用户会话提供支持
    静态文件(Static Files) ��对服务静态文件和目录浏览提供支持

    个人博客

    我的个人博客

    相关文章

      网友评论

        本文标题:ASP.NET Core基本原理(2)-中间件

        本文链接:https://www.haomeiwen.com/subject/qyhqmttx.html