ASP.NET Core 中文文档 第三章 原理(2)中间件

原文:Middleware
作者:Steve Smith and Rick Anderson
翻译:刘怡(AlexLEWIS)
校对:许登洋(Seay)

章节:

  • 什么是中间件

  • 用 IApplicationBuilder 创建中间件管道

  • 内置中间件

  • 编写中间件

  • 扩展资源

查看或下载样例代码

什么是中间件

中间件是用于组成应用程序管道来处理请求和响应的组件。管道内的每一个组件都可以选择是否将请求交给下一个组件、并在管道中调用下一个组件之前和之后执行某些操作。请求委托被用来建立请求管道,请求委托处理每一个 HTTP 请求。

请求委托通过使用 IApplicationBuilder 类型的 Run、Map 以及 Use 扩展方法来配置,并在 Startup 类中传给 Configure 方法 。每个单独的请求委托都可以被指定为一个内嵌匿名方法,或其定义在一个可重用的类中。这些可重用的类被称作 中间件 或 中间件组件。每个位于请求管道内的中间件组件负责调用管道中下一个组件,或适时短路调用链。

Migrating HTTP Modules to Middleware 解释了请求管道在 ASP.NET Core 和之前版本之间的区别,并提供了更多中间件样例。

用 IApplicationBuilder 创建中间件管道

ASP.NET 请求管道由一系列的请求委托所构成,它们一个接着一个被调用,如图所示(该执行线程按黑色箭头的顺序执行):

每个委托在下一个委托之前和之后都有机会执行操作。任何委托都能选择停止传递到下一个委托,转而自己处理该请求。这被叫做请求管道的短路,而且是一种有意义的设计,因为它可以避免不必要的工作。比方说,一个授权(authorization)中间件只有在通过身份验证之后才调用下一个委托,否则它就会被短路并返回 “Not Authorized” 的响应。异常处理委托需要在管道的早期被调用,这样它们就能够捕捉到发生在管道内更深层次出现的异常了。

你可以看一下 Visual Studio 2015 附带的默认 Web 站点模板关于请求管道设置的例子。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();//手工高亮// Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715app.UseMvc(routes =>//手工高亮{routes.MapRoute(name: "default",            template: "{controller=Home}/{action=Index}/{id?}");}); }

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

静态文件模块 不提供授权检查,由它提供的任何文件,包括那些位于wwwroot 下的文件都是公开的可被访问的。如果你想基于授权来提供这些文件:

  1. 将它们存放在 wwwroot 外面以及任何静态文件中间件都可访问得到的目录。

  2. 利用控制器操作来判断授权是否允许,如果允许则通过返回 FileResult 来提供它们。

被静态文件模块处理的请求会在管道中被短路(参见 Working with Static Files)。如果该请求不是由静态文件模块处理,那么它就会被传给 Identity 模块 执行身份验证。如果未通过身份验证,则管道将被短路。如果请求的身份验证没有失败,则管道的最后一站是 MVC 框架。

注意
你添加中间件组件的顺序通常会影响到它们处理请求的顺序,然后在响应时则以相反的顺序返回。这对应用程序安全、性能和功能很关键。在上面的代码中,静态文件中间件 在管道的早期被调用,这样就能处理并及时短路管道,以避免请求走到不必要的组件中。身份验证中间件被添加在任何需要身份认证的处理请求的前面。异常处理必须被注册在其它中间件之前以便捕获其它组件的异常。

最简单的 ASP.NET 应用程序是使用单个请求委托来处理所有请求。事实上在这种情况下并不存在所谓的“管道”,调用单个匿名函数以相应每个 HTTP 请求。

复制代码

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

第一个 App.Run 委托中断了管道。在下面的例子中,只有第一个委托(“Hello, World!”)会被运行。

复制代码

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);//手工高亮}); }

警告
应当避免在修改了 HttpResponse 之后还调用管道内下一个会修改响应的组件,从而导致它被送到客户端处。

提示
当应用程序运行的环境设置为 LogInline 时,这个 ConfigureLogInline 方法就会被调动。要了解更多请访问环境 Working with Multiple Environments 一章。本文剩下的篇幅将使用变化的 Configure[Environment] 来展示不同的选项。 Visual Studio 中运行示例代码的最简单办法是使用 web 命令,该命令由 project.json 文件所配置。也可参考 Application Startup 。

在上例中,调用 await next.Invoke() 将会调用下一个委托await context.Response.WriteAsync("Hello from " + _environment);。客户端将收到预期的响应(“Hello from LogInline”),同时服务端这边的控制台将先后输出如下信息:

Run,Map 与 Use

你可以使用 Run、Map 和 Use 配置 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);}); }

注意
IApplicationBuilder 接口向外暴露了一个 Use 方法,因此从技术上来说它们并不完全是 扩展 方法。

我们已经看了几个关于如何通过 Use 构建请求管道的例子,同时约定了 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 定义查询字符串值)将被第 17 行所定义的委托处理。

你也可以嵌套映射:

复制代码

app.Map("/level1", level1App => {level1App.Map("/level2a", level2AApp => {        // "/level1/level2a"//...});level1App.Map("/level2b", level2BApp => {        // "/level1/level2b"//...}); });

内置中间件

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

中间件描述
身份验证(Authentication)提供身份验证支持。
跨域资源共享(CORS)配置跨域资源共享。CORS 全称为 Cross-Origin Resource Sharing。
路由(Routing)定义和约定请求路由。
会话(Session)提供对管理用户会话(session)的支持。
静态文件提供对静态文件服务于目录浏览的支持。

编写中间件

CodeLabs 中间件教程 提供了一个清晰介绍用于编写中间件。

对于更复杂的请求处理功能,ASP.NET 团队推荐在他们自己的类中实现中间件,并暴露 IApplicationBuilder 扩展方法,这样就能通过 Configure 方法来被调用。之前演示的简易日志中间件就能被转换为一个中间件类(middleware class):只要在其构造函数中获得下一个 RequestDelegate 并提供一个 Invoke方法,如下所示:

复制代码

using System.Threading.Tasks;using Microsoft.AspNetCore.Http;using Microsoft.Extensions.Logging;namespace MiddlewareSample{    public class RequestLoggerMiddleware{        private readonly RequestDelegate _next;        private readonly ILogger _logger;        public RequestLoggerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)//手工高亮        {_next = next;_logger = loggerFactory.CreateLogger<RequestLoggerMiddleware>();}        public async Task Invoke(HttpContext context)//手工高亮        {_logger.LogInformation("Handling request: " + context.Request.Path);            await _next.Invoke(context);_logger.LogInformation("Finished handling request.");}} }

中间件遵循 显式依赖原则 并在其构造函数中暴露所有依赖项。中间件能够利用到 UseMiddleware  扩展方法的优势,直接通过它们的构造函数注入服务,就像下面的例子所示。依赖注入服务是自动完成填充的,扩展所用到的 params 参数数组被用于非注入参数。

复制代码

public static class RequestLoggerExtensions{    public static IApplicationBuilder UseRequestLogger(this IApplicationBuilder builder)    {        return builder.UseMiddleware<RequestLoggerMiddleware>();//手工高亮} }

通过使用扩展方法和相关中间件类,Configure 方法变得非常简洁和高可读性。

复制代码

public void ConfigureLogMiddleware(IApplicationBuilder app,    ILoggerFactory loggerfactory){loggerfactory.AddConsole(minLevel: LogLevel.Information);app.UseRequestLogger();//手工高亮app.Run(async context =>{        await context.Response.WriteAsync("Hello from " + _environment);}); }

尽管 RequestLoggerMiddleware 在其构造函数中需要 ILoggerFactory 参数,但无论是 Startup 类还是 UseRequestLogger 扩展方法都不需要显式依赖之。相反,它将自动地通过内置的 UseMiddleware<T> 来执行依赖注入以提供之。

测试中间件(通过给 LogMiddleware 设置 Hosting:Environment 环境变量)会输出下图的结果(当时用了 WebListener 时):

注意
UseStaticFiles 扩展方法(该方法会创建 StaticFileMiddleware)同样也使用了 UseMiddleware<T>。所以除了 StaticFileOptions 参数被传入之外,构造函数的其他参数都由 UseMiddleware<T> 和依赖注入所提供。

原文地址:http://www.cnblogs.com/dotNETCoreSG/p/aspnetcore-3_2-middleware.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关联分析:FP-Growth算法

转载自 关联分析&#xff1a;FP-Growth算法关联分析又称关联挖掘&#xff0c;就是在交易数据、关系数据或其他信息载体中&#xff0c;查找存在于项目集合或对象集合之间的频繁模式、关联、相关性或因果结构。关联分析的一个典型例子是购物篮分析。通过发现顾客放入购物篮中不同…

sql server链接查询

一、连接结果集中有多个表的信息时用连接查询1.内连接:多个表根据公共列连接&#xff0c;符合条件的显示&#xff0c;不符合条件的不显示 2.外连接:多个表根据公共列连接&#xff0c;显示一个表中的所有信息&#xff0c;另个表中中符合条件的信息&#xff0c;不符合条件的用nul…

ASP.NET Core 中文文档 第三章 原理(1)应用程序启动

原文&#xff1a;Application Startup作者&#xff1a;Steve Smith翻译&#xff1a;刘怡(AlexLEWIS)校对&#xff1a;谢炀(kiler398)、许登洋(Seay) ASP.NET Core 为你的应用程序提供了处理每个请求的完整控制。Startup 类是应用程序的入口&#xff08;entry point&#xff09;…

基于内容的推荐(Content-based Recommendations)

转载自 基于内容的推荐&#xff08;Content-based Recommendations&#xff09;Collaborative Filtering Recommendations (协同过滤&#xff0c;简称CF) 是目前最流行的推荐方法&#xff0c;在研究界和工业界得到大量使用。但是&#xff0c;工业界真正使用的系统一般都不会只…

[置顶]动态网页开发基础【笔记】

一、C/S结构和B/S结构1.C/S&#xff08;Client/Server&#xff09;:客户端服务程序&#xff0c;控制台程序&#xff0c;window应用2.B/S(Browser/Server):浏览器服务程序[java:jsp应用&#xff1b;C#:asp.net],web应用程序区别&#xff1a;C/S:客户端和服务器端都需要开发&…

ASP.NET Core 中文文档 第三章 原理(3)静态文件处理

原文&#xff1a;Working with Static Files作者&#xff1a;Rick Anderson翻译&#xff1a;刘怡(AlexLEWIS)校对&#xff1a;谢炀(kiler398)、许登洋(Seay)、孟帅洋(书缘) 静态文件&#xff08;static files&#xff09;&#xff0c;诸如 HTML、CSS、图片和 JavaScript 之类的…

使用IDEA Maven搭建Mybatis环境

本文是使用IDEA Maven搭建Mybatis环境 涉及到的搭配环境 JDK1.8 Maven3.6.3 IDEA2019 MySQL数据库 一、Maven介绍 Maven 是一个项目管理工具&#xff0c;可以对 Java 项目进行构建、依赖管理&#xff0c;是一个自动化构建工具。 自动化构建工具&#xff1a;将原材料&…

常用推荐算法

转载自 常用推荐算法在推荐系统简介中&#xff0c;我们给出了推荐系统的一般框架。很明显&#xff0c;推荐方法是整个推荐系统中最核心、最关键的部分&#xff0c;很大程度上决定了推荐系统性能的优劣。目前&#xff0c;主要的推荐方法包括&#xff1a;基于内容推荐、协同过滤…

不该活着的SqlHelper和DBHelper

前言&#xff1a; 还记得刚学ADO.NET的情景么&#xff1f; 还记得当年是怎么从ADO.NET被忽悠到用SqlHelper的么&#xff1f; 话说从入门到走上工作岗位那些年&#xff0c;我们就一直被纯纯地教导或引导&#xff0c;ADO.NET太原始&#xff0c;得封装成SqlHelper或DBHelper......…

使用IDEA配置Mybatis-Plus框架

使用IDEA配置Mybatis-Plus框架 本文是以使用IDEA配置Mybatis-Plus框架作为简单的讲解。 所涉及到的应用&#xff1a; IDEA2019Mybatis-Plus框架Mysql数据库Maven3.6.3jdk1.8 一、什么是Mybatis-Plus框架&#xff1f; MyBatis-Plus(简称MP)是一个MyBatis的增强工具&#xff…

css实现一级下拉菜单

涉及到的图片请到这里下载 涉及到的图片请到这里下载 涉及到的图片请到这里下载 html代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns"…

UserCF,基于用户的协同过滤算法

转载自 UserCF,基于用户的协同过滤算法UserCF&#xff1a;User Collaboration Filter&#xff0c;基于用户的协同过滤 算法核心思想&#xff1a;在一个在线推荐系统中&#xff0c;当用户A需要个性化推荐时&#xff0c;可以先找到和他有相似兴趣的其它用户&#xff0c;然后…

用BenchmarkDotNet给C#程序做性能测试

BenchmarkDotNet是一个用MIT协议开源的C#程序性能测试的一个库&#xff0c;非常简单易用。 用法 安装NuGet包&#xff0c;BenchmarkDotNet在需要做性能测试的方法前加上属性[Benchmark]。在Main函数调用性能测试var summary BenchmarkRunner.Run<Md5VsSha256>();。 工作…

ItemCF,基于物品的协同过滤算法

转载自 ItemCF,基于物品的协同过滤算法ItemCF&#xff1a;Item Collaboration Filter&#xff0c;基于物品的协同过滤 算法核心思想&#xff1a;给用户推荐那些和他们之前喜欢的物品相似的物品。 比如&#xff0c;用户A之前买过《数据挖掘导论》&#xff0c;该算法会根据此行…

用Swashbuckle给ASP.NET Core的项目自动生成Swagger的API帮助文档

Swagger是一个描述RESTful的Web API的规范和框架。如果使用ASP.NET的话&#xff0c;可以用Swashbuckle来自动生成Swagger,具体参考如何使 WebAPI 自动生成漂亮又实用在线API文档。下面详细的介绍一下如何给ASP.NET Core的项目自动生成Swagger的API帮助文档。 创建ASP.NET Core的…

涨知识 | 100个中国最难读的地名,第一个就折了

内容来源网络&#xff0c;侵删&#xff01; 1.浙江丽水的丽&#xff0c;不读l&#xff0c;读l。 2.浙江台州、天台的台&#xff0c;不读ti&#xff0c;读tāi。 3.浙江嵊州的嵊&#xff0c;读音为shng。 4.浙江鄞县的鄞&#xff0c;读音为yn&#xff0c;不读jn。 5.浙江乐清的乐…

JSP 教程

JSP 教程JSP 教程JSP 简介JSP 开发环境搭建Eclipse JSP/ServletJSP 结构JSP 生命周期JSP 语法JSP 指令JSP 动作元素JSP 隐式对象JSP 客户端请求JSP 服务器响应JSP HTTP 状态码JSP 表单处理JSP 过滤器JSP Cookie 处理JSP SessionJSP 文件上传JSP 日期处理JSP 页面重定向JSP 点击…

IronPython项目有了新负责人

运行IronPython项目的社区领导职责已经交给了Alex Earl和Benedikt Eggers。之前的负责人Jeff Hardy自微软在2010年不再积极参与以来一直负责项目的运行。 Jeff Hardy写道&#xff1a; 应该已经相当明显了&#xff0c;IronPython项目有点停滞不前&#xff0c;那主要是我的错&…

修改Tomcat编码方式的两种方法

转载自 修改Tomcat编码方式的两种方法方法一&#xff1a;推荐&#xff0c;不会影响到其它项目 见我的另一篇博客&#xff1a;http://www.cnblogs.com/x_wukong/p/3292664.html 修改方法&#xff1a; 修改tomcat下的conf/server.xml文件&#xff0c;找到Connector标签&#x…

Microsoft宣布.NET Core支持计划

微软宣布针对.NET Core、ASP.NET Core和Entity Framework Core的支持计划。据来自微软的Lee Coward介绍&#xff0c;公司计划使用一种双管齐下的方法来管理这些系统的发布。该计划回答了微软开源计划背后的其中一个主要问题——开发人员可以在多长时间内依赖微软向这些年轻的项…