Asp.Net Core 中间件
什么是中间件(Middleware)?
中间件是组装到应用程序管道中以处理请求和响应的软件。 每个组件:
- 选择是否将请求传递给管道中的下一个组件。
- 可以在调用管道中的下一个组件之前和之后执行工作。
请求委托(Request delegates)用于构建请求管道,处理每个HTTP请求。
请求委托使用Run
,Map
和Use
扩展方法进行配置。单独的请求委托可以以内联匿名方法(称为内联中间件)指定,或者可以在可重用的类中定义它。这些可重用的类和内联匿名方法是中间件或中间件组件。请求流程中的每个中间件组件都负责调用流水线中的下一个组件,如果适当,则负责链接短路。
将HTTP模块迁移到中间件解释了ASP.NET Core和以前版本(ASP.NET)中的请求管道之间的区别,并提供了更多的中间件示例。
使用 IApplicationBuilder 创建中间件管道
ASP.NET Core请求流程由一系列请求委托组成,如下图所示(执行流程遵循黑色箭头):
每个委托可以在下一个委托之前和之后执行操作。委托还可以决定不将请求传递给下一个委托,这称为请求管道的短路。短路通常是可取的,因为它避免了不必要的工作。例如,静态文件中间件可以返回一个静态文件的请求,并使管道的其余部分短路。需要在管道早期调用异常处理委托,因此它们可以捕获后面管道的异常。
最简单的可能是ASP.NET Core应用程序建立一个请求的委托,处理所有的请求。此案例不包含实际的请求管道。相反,针对每个HTTP请求都调用一个匿名方法。
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.Http;
- public class Startup
- {
- public void Configure(IApplicationBuilder app)
- {
- app.Run(async context =>
- {
- await context.Response.WriteAsync("Hello, World!");
- });
- }
- }
第一个 app.Run
委托终止管道。
如下代码:
通过浏览器访问,发现确实在第一个app.Run
终止了管道。
可以将多个请求委托与app.Use
连接在一起。 next
参数表示管道中的下一个委托。 (请记住,您可以通过不调用下一个参数来结束流水线。)通常可以在下一个委托之前和之后执行操作,如下例所示:
- public class Startup
- {
- public void Configure(IApplicationBuilder app)
- {
- app.Use(async (context, next) =>
- {
- await context.Response.WriteAsync("进入第一个委托 执行下一个委托之前\r\n");
- //调用管道中的下一个委托
- await next.Invoke();
- await context.Response.WriteAsync("结束第一个委托 执行下一个委托之后\r\n");
- });
- app.Run(async context =>
- {
- await context.Response.WriteAsync("进入第二个委托\r\n");
- await context.Response.WriteAsync("Hello from 2nd delegate.\r\n");
- await context.Response.WriteAsync("结束第二个委托\r\n");
- });
- }
- }
使用浏览器访问有如下结果:
可以看出请求委托的执行顺序是遵循上面的流程图的。
注意:
响应发送到客户端后,请勿调用next.Invoke
。 响应开始之后,对HttpResponse的更改将抛出异常。 例如,设置响应头,状态代码等更改将会引发异常。在调用next
之后写入响应体。
可能导致协议违规。 例如,写入超过
content-length
所述内容长度。可能会破坏响应内容格式。 例如,将HTML页脚写入CSS文件。
HttpResponse.HasStarted是一个有用的提示,指示是否已发送响应头和/或正文已写入。
顺序
在Startup。Configure
方法中添加中间件组件的顺序定义了在请求上调用它们的顺序,以及响应的相反顺序。 此排序对于安全性,性能和功能至关重要。
Startup.Configure
方法(如下所示)添加了以下中间件组件:
- 异常/错误处理
- 静态文件服务
- 身份认证
- MVC
- public void Configure(IApplicationBuilder app)
- {
- app.UseExceptionHandler("/Home/Error"); // Call first to catch exceptions
- // thrown in the following middleware.
- app.UseStaticFiles(); // Return static files and end pipeline.
- app.UseAuthentication(); // Authenticate before you access
- // secure resources.
- app.UseMvcWithDefaultRoute(); // Add MVC to the request pipeline.
- }
上面的代码,UseExceptionHandler
是添加到管道中的第一个中间件组件,因此它捕获以后调用中发生的任何异常。
静态文件中间件在管道中提前调用,因此可以处理请求和短路,而无需通过剩余的组件。 静态文件中间件不提供授权检查。 由其提供的任何文件,包括wwwroot下的文件都是公开的。
如果请求没有被静态文件中间件处理,它将被传递给执行身份验证的Identity中间件(app.UseAuthentication)。 身份不会使未经身份验证的请求发生短路。 虽然身份认证请求,但授权(和拒绝)仅在MVC选择特定的Razor页面或控制器和操作之后才会发生。
授权(和拒绝)仅在MVC选择特定的Razor页面或Controller和Action之后才会发生。
以下示例演示了中间件顺序,其中静态文件的请求在响应压缩中间件之前由静态文件中间件处理。 静态文件不会按照中间件的顺序进行压缩。 来自UseMvcWithDefaultRoute的MVC响应可以被压缩。
- public void Configure(IApplicationBuilder app)
- {
- app.UseStaticFiles(); // Static files not compressed
- app.UseResponseCompression();
- app.UseMvcWithDefaultRoute();
- }
Use, Run, 和 Map
可以使用Use
,Run
和Map
配置HTTP管道。Use
方法可以使管道短路(即,可以不调用下一个请求委托)。Run
方法是一个约定, 并且一些中间件组件可能暴露在管道末端运行的Run [Middleware]方法。Map*
扩展用作分支管道的约定。映射根据给定的请求路径的匹配来分支请求流水线,如果请求路径以给定路径开始,则执行分支。
- public class Startup
- {
- private static void HandleMapTest1(IApplicationBuilder app)
- {
- app.Run(async context =>
- {
- await context.Response.WriteAsync("Map Test 1");
- });
- }
- private static void HandleMapTest2(IApplicationBuilder app)
- {
- app.Run(async context =>
- {
- await context.Response.WriteAsync("Map Test 2");
- });
- }
- public void Configure(IApplicationBuilder app)
- {
- app.Map("/map1", HandleMapTest1);
- app.Map("/map2", HandleMapTest2);
- app.Run(async context =>
- {
- await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
- });
- }
- }
下表显示了使用以前代码的 http://localhost:19219 的请求和响应:
请求 | 响应 |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/map1 | Map Test 1 |
localhost:1234/map2 | Map Test 2 |
localhost:1234/map3 | Hello from non-Map delegate. |
当使用Map时,匹配的路径段将从HttpRequest.Path
中删除,并为每个请求追加到Http Request.PathBase
。
MapWhen
根据给定谓词的结果分支请求流水线。 任何类型为Func<HttpContext,bool>
的谓词都可用于将请求映射到管道的新分支。 在以下示例中,谓词用于检测查询字符串变量分支的存在:
- public class Startup
- {
- private static void HandleBranch(IApplicationBuilder app)
- {
- app.Run(async context =>
- {
- var branchVer = context.Request.Query["branch"];
- await context.Response.WriteAsync($"Branch used = {branchVer}");
- });
- }
- public void Configure(IApplicationBuilder app)
- {
- app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
- HandleBranch);
- app.Run(async context =>
- {
- await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
- });
- }
以下下表显示了使用上面代码 http://localhost:19219 的请求和响应:
请求 | 响应 |
---|---|
localhost:1234 | Hello from non-Map delegate. |
localhost:1234/?branch=1 | Branch used = master |
Map
支持嵌套,例如:
- app.Map("/level1", level1App => {
- level1App.Map("/level2a", level2AApp => {
- // "/level1/level2a"
- //...
- });
- level1App.Map("/level2b", level2BApp => {
- // "/level1/level2b"
- //...
- });
- });
Map
也可以一次匹配多个片段,例如:
- app.Map("/level1/level2", HandleMultiSeg);
内置中间件
ASP.NET Core附带以下中间件组件:
中间件 | 描述 |
---|---|
Authentication | 提供身份验证支持 |
CORS | 配置跨域资源共享 |
Response Caching | 提供缓存响应支持 |
Response Compression | 提供响应压缩支持 |
Routing | 定义和约束请求路由 |
Session | 提供用户会话管理 |
Static Files | 为静态文件和目录浏览提供服务提供支持 |
URL Rewriting Middleware | 用于重写 Url,并将请求重定向的支持 |
编写中间件
中间件通常封装在一个类中,并使用扩展方法进行暴露。 查看以下中间件,它从查询字符串设置当前请求的Culture:
- public class Startup
- {
- public void Configure(IApplicationBuilder app)
- {
- app.Use((context, next) =>
- {
- var cultureQuery = context.Request.Query["culture"];
- if (!string.IsNullOrWhiteSpace(cultureQuery))
- {
- var culture = new CultureInfo(cultureQuery);
- CultureInfo.CurrentCulture = culture;
- CultureInfo.CurrentUICulture = culture;
- }
- // Call the next delegate/middleware in the pipeline
- return next();
- });
- app.Run(async (context) =>
- {
- await context.Response.WriteAsync(
- $"Hello {CultureInfo.CurrentCulture.DisplayName}");
- });
- }
- }
您可以通过传递Culture来测试中间件,例如 http://localhost:19219/?culture=zh-CN
以下代码将中间件委托移动到一个类:
- using Microsoft.AspNetCore.Http;
- using System.Globalization;
- using System.Threading.Tasks;
- namespace Culture
- {
- public class RequestCultureMiddleware
- {
- private readonly RequestDelegate _next;
- public RequestCultureMiddleware(RequestDelegate next)
- {
- _next = next;
- }
- public Task Invoke(HttpContext context)
- {
- var cultureQuery = context.Request.Query["culture"];
- if (!string.IsNullOrWhiteSpace(cultureQuery))
- {
- var culture = new CultureInfo(cultureQuery);
- CultureInfo.CurrentCulture = culture;
- CultureInfo.CurrentUICulture = culture;
- }
- // Call the next delegate/middleware in the pipeline
- return this._next(context);
- }
- }
- }
以下通过IApplicationBuilder的扩展方法暴露中间件
- using Microsoft.AspNetCore.Builder;
- namespace Culture
- {
- public static class RequestCultureMiddlewareExtensions
- {
- public static IApplicationBuilder UseRequestCulture(
- this IApplicationBuilder builder)
- {
- return builder.UseMiddleware<RequestCultureMiddleware>();
- }
- }
- }
以下代码从Configure
调用中间件:
- public class Startup
- {
- public void Configure(IApplicationBuilder app)
- {
- app.UseRequestCulture();
- app.Run(async (context) =>
- {
- await context.Response.WriteAsync(
- $"Hello {CultureInfo.CurrentCulture.DisplayName}");
- });
- }
- }
中间件应该遵循显式依赖原则,通过在其构造函数中暴露其依赖关系。 中间件在应用程序生命周期构建一次。 如果您需要在请求中与中间件共享服务,请参阅以下请求相关性。
中间件组件可以通过构造方法参数来解析依赖注入的依赖关系。 UseMiddleware也可以直接接受其他参数。
每个请求的依赖关系
因为中间件是在应用程序启动时构建的,而不是每个请求,所以在每个请求期间,中间件构造函数使用的作用域生命周期服务不会与其他依赖注入类型共享。 如果您必须在中间件和其他类型之间共享作用域服务,请将这些服务添加到Invoke方法的签名中。 Invoke方法可以接受由依赖注入填充的其他参数。 例如:
- public class MyMiddleware
- {
- private readonly RequestDelegate _next;
- public MyMiddleware(RequestDelegate next)
- {
- _next = next;
- }
- public async Task Invoke(HttpContext httpContext, IMyScopedService svc)
- {
- svc.MyProperty = ;
- await _next(httpContext);
- }
- }
Asp.Net Core 中间件的更多相关文章
- ASP.NET Core 中间件Diagnostics使用
ASP.NET Core 中间件(Middleware)Diagnostics使用.对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware). Diagnost ...
- ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析
ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析. 本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host. 因 ...
- [转]ASP.NET Core 中间件详解及项目实战
本文转自:http://www.cnblogs.com/savorboard/p/5586229.html 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际 ...
- 如何一秒钟从头构建一个 ASP.NET Core 中间件
前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...
- ASP.NET Core中间件实现分布式 Session
1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件的配置 1.2. 依赖注入中间件 1.3. Cookies ...
- ASP.NET Core 入门教程 9、ASP.NET Core 中间件(Middleware)入门
一.前言 1.本教程主要内容 ASP.NET Core 中间件介绍 通过自定义 ASP.NET Core 中间件实现请求验签 2.本教程环境信息 软件/环境 说明 操作系统 Windows 10 SD ...
- ASP.NETCore学习记录(二) —— ASP.NET Core 中间件
ASP.NET Core 中间件 目录: 什么是中间件 ? IApplicationBuilder 使用 IApplicationBuilder 创建中间件 Run.Map 与 Use 方法 实战中间 ...
- ASP.NET Core 中间件基本用法
ASP.NET Core 中间件 ASP.NET Core的处理流程是一个管道,而中间件是装配到管道中的用于处理请求和响应的组件.中间件按照装配的先后顺序执行,并决定是否进入下一个组件.中间件管道的处 ...
- (4)ASP.NET Core 中间件
1.前言 整个HTTP Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline).而中间件(middleware)则是一种装配到请求管道以 ...
- ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 中间件 上一章节中,我们我们有讲到 Startup 类中的 Confi ...
随机推荐
- 【零基础】搞定LAMP(linux、apache、mysql、php)环境安装图文教程(基于centos7)
一.前言 LAMP即:Linux.Apache.Mysql.Php,也就是在linux系统下运行php网站代码,使用的数据库是mysql.web服务软件是apache.之所以存在LAMP这种说法,倒不 ...
- KERNEL_SECURITY_CHECK_FAILURE
出现错误提示重装系统可以解决问题,但不需要重装系统.win8错误提示:KERNEL_SECURITY_CHECK_FAILURE提示对应错误代码:0x00000139 (0x00000003, 0x8 ...
- koa 项目实战(八)生成token
1.安装模块 npm install jsonwebtoken --save 2.引用 const jwt = require('jsonwebtoken'); ... // 返回token cons ...
- 用socket.io实现websocket的一个简单例子
socket.io 是基于 webSocket 构建的跨浏览器的实时应用. 逛博客发现几个比较好的 一.用socket.io实现websocket的一个简单例子 http://biyeah.iteye ...
- nginx开启目录浏览,解决中文乱码问题
nginx开启目录浏览,解决中文乱码问题 方法如下: server { listen 80; #listen [::]:80; server_name gongzi.liwenhui.xin gz.l ...
- Android studio怎么使用git代码文件逐行追溯
在Android studio中集成了相当多的工具用于管理代码,应该现在经常使用的git的方式来管理管理,用于上传代码或者进行下载代码库中,而在git中进行管理的话,那么就可以进行历史的记录信息,如果 ...
- ZeroMQ——一个轻量级的消息通信组件 C#
ZeroMQ——一个轻量级的消息通信组件 ZeroMQ是一个轻量级的消息通信组件,尽管名字中包含了"MQ",严格上来讲ZeroMQ并不是"消息队列/消息中间件" ...
- Hibernate 的一些注解配置
网上参考资料很多,但总是不符合自身习惯,遂记录下来. 一对多的关系 如class与student的关系 class中 @OneToMany(mappedBy = "class") ...
- JavaScript(7)——DOM
什么是 DOM? DOM是 Document Object Model(文档对象模型)的缩写 DOM是 W3C(万维网联盟)的标准. DOM 定义了访问 HTML 和 XML 文档的标准: “W3C ...
- Array.prototype.filter()
1. filter() 方法:创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素 ——filter() 不会对空数组进行检测 ——filter() 不会改变原始数组 2. 语法: ...