本文为官方文档译文,官方文档现已非机器翻译 https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1

什么是中间件(Middleware)?

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

  • 选择是否将请求传递给管道中的下一个组件。
  • 可以在调用管道中的下一个组件之前和之后执行工作。

请求委托(Request delegates)用于构建请求管道,处理每个HTTP请求。

请求委托使用RunMapUse扩展方法进行配置。单独的请求委托可以以内联匿名方法(称为内联中间件)指定,或者可以在可重用的类中定义它。这些可重用的类和内联匿名方法是中间件或中间件组件。请求流程中的每个中间件组件都负责调用流水线中的下一个组件,如果适当,则负责链接短路。

将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方法(如下所示)添加了以下中间件组件:

  1. 异常/错误处理
  2. 静态文件服务
  3. 身份认证
  4. 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

你可以使用UseRunMap配置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 = 1000;
await _next(httpContext);
}
}

ASP.NET Core 中间件(Middleware)详解的更多相关文章

  1. ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析

    ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析. 本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host. 因 ...

  2. 【转】asp.net core环境变量详解

    asp.net core环境变量详解 环境变量详解 Windows操作系统的环境变量在哪设置应该都知道了. Linux(centos版本)的环境变量在/etc/profile里面进行设置.用户级的环境 ...

  3. ASP.NET Core 入门教程 9、ASP.NET Core 中间件(Middleware)入门

    一.前言 1.本教程主要内容 ASP.NET Core 中间件介绍 通过自定义 ASP.NET Core 中间件实现请求验签 2.本教程环境信息 软件/环境 说明 操作系统 Windows 10 SD ...

  4. ASP.NET Core 入门笔记10,ASP.NET Core 中间件(Middleware)入门

    一.前言 1.本教程主要内容 ASP.NET Core 中间件介绍 通过自定义 ASP.NET Core 中间件实现请求验签 2.本教程环境信息 软件/环境 说明 操作系统 Windows 10 SD ...

  5. ASP.NET Core真实管道详解[1]:中间件是个什么东西?

    ASP.NET Core管道虽然在结构组成上显得非常简单,但是在具体实现上却涉及到太多的对象,所以我们在 <ASP.NET Core管道深度剖析[共4篇]> 中围绕着一个经过极度简化的模拟 ...

  6. ASP.NET Core -中间件(Middleware)使用

    ASP.NET Core开发,开发并使用中间件(Middleware). 中间件是被组装成一个应用程序管道来处理请求和响应的软件组件. 每个组件选择是否传递给管道中的下一个组件的请求,并能之前和下一组 ...

  7. ASP.NET Core真实管道详解[1]

    ASP.NET Core管道虽然在结构组成上显得非常简单,但是在具体实现上却涉及到太多的对象,所以我们在 <ASP.NET Core管道深度剖析[共4篇]> 中围绕着一个经过极度简化的模拟 ...

  8. ASP.NET Core真实管道详解[2]:Server是如何完成针对请求的监听、接收与响应的【上】

    Server是ASP .NET Core管道的第一个节点,负责完整请求的监听和接收,最终对请求的响应同样也由它完成.Server是我们对所有实现了IServer接口的所有类型以及对应对象的统称,如下面 ...

  9. ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解 (转载)

    “传导体” HttpContext 要理解 HttpContext 是干嘛的,首先,看图 图一 内网访问程序 图二 反向代理访问程序 ASP.NET Core 程序中,Kestrel 是一个基于 li ...

  10. ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解

    笔者没有学 ASP.NET,直接学 ASP.NET Core ,学完 ASP.NET Core MVC 基础后,开始学习 ASP.NET Core 的运行原理.发现应用程序有一个非常主要的 “传导体” ...

随机推荐

  1. SQL系统函数的使用(实验五)

    SQL系统函数的使用(试验5) 函数在查询语句中的使用 查询员工的姓名和日工资(保留1位小数): 查询并显示部门号为01和02的所有员工的姓名首字及岗位: 查询并显示所有员工的姓名及工龄: 查询199 ...

  2. 《天书夜读:从汇编语言到windows内核编程》一 汇编指令与C语言

    1. Debug模式下,VC++6.0下断点运行,按CTRL+F11可查看汇编代码:另外可以用cl /c /FAs YourCppFile.cpp命令行在同目录生成YourCppFile.asm汇编文 ...

  3. Java多线程与并发模型之锁

    这是一篇总结Java多线程开发的长文.文章是从Java创建之初就存在的synchronized关键字引入,对Java多线程和并发模型进行了探讨.希望通过此篇内容的解读能帮助Java开发者更好的理清Ja ...

  4. HTML基础--元素类型及类型转换

    元素类型及类型转换 一.XHTML元素分类 根据css显示分类,XHTML元素被分为三种类型:块状元素,内联元素,可变元素 1.块状元素(block element) 1)块状元素在网页中就是以块的形 ...

  5. T-SQL的进阶:超越基本级别3:构建相关子查询——701小组

    T-SQL的进阶:超越基本级别3:构建相关子查询 格雷戈里·拉森,2014/03/05 原文链接: http://www.sqlservercentral.com/articles/Stairway+ ...

  6. [读书笔记]javascript语言精粹'

    人比较笨,以前只做项目,案例,然而一些javascript的很多理论不知道该怎么描述,所以最近开启一波读书之旅: 标识符 1.定义 标识符以字母开头,可能后面跟上一个或多个字母.数字或者下划线. 2. ...

  7. You may rarely look at it. But you'll always feel it

    You may rarely look at it. But you'll always feel it

  8. Less 原理

    Less 原理 Less 包含一套自定义的语法及一个解析器,用户根据这些语法定义自己的样式规则,这些规则最终会通过解析器,编译生成对应的 CSS 文件. Less 并没有裁剪 CSS 原有的特性,更不 ...

  9. 变位词(0029)-swustoj

    变位词(0029)水题 变位词如果两个单词的组成字母完全相同,只是字母的排列顺序不一样,则它们就是变位词,两个单词相同也被认为是变位词.如tea 与eat , nic 与cin, ddc与dcd, a ...

  10. 51Nod 1001 数组中和等于K的数对 Set

    给出一个整数K和一个无序数组A,A的元素为N个互不相同的整数,找出数组A中所有和等于K的数对.例如K = 8,数组A:{-1,6,5,3,4,2,9,0,8},所有和等于8的数对包括(-1,9),(0 ...