ASP.NET Core 中间件

ASP.NET Core的处理流程是一个管道,而中间件是装配到管道中的用于处理请求和响应的组件。中间件按照装配的先后顺序执行,并决定是否进入下一个组件。中间件管道的处理流程如下图(图片来源于官网):

管道式的处理方式,更加方便我们对程序进行扩展。

使用中间件

ASP.NET Core中间件模型是我们能够快捷的开发自己的中间件,完成对应用的扩展,我们先从一个简单的例子了解一下中间件的开发。

Run

首先,我们创建一个ASP.NET Core 应用,在Startup.cs中有如下代码:

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

这段代码中,使用Run方法运行一个委托,这就是最简单的中间件,它拦截了所有请求,返回一段文本作为响应。Run委托终止了管道的运行,因此也叫作终端中间件

Use

我们再看另外一个例子:

app.Use(async (context, next) =>
{
//Do something here //Invoke next middleware
await next.Invoke(); //Do something here });

这段代码中,使用Use方法运行一个委托,我们可以在Next调用之前和之后分别执行自定义的代码,从而可以方便的进行日志记录等工作。这段代码中,使用next.Invoke()方法调用下一个中间件,从而将中间件管道连贯起来;如果不调用next.Invoke()方法,则会造成管道短路

Map和MapWhen

处理上面两种方式,ASP.NET Core 还可以使用Map创建基于路径匹配的分支、使用MapWhen创建基于条件的分支。代码如下:

private static void HandleMap(IApplicationBuilder app)
{
app.Run(async context =>
{
await context.Response.WriteAsync("Handle Map");
});
} 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, IHostingEnvironment env)
{
app.Map("/map", HandleMap); app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
HandleBranch); app.Run(async context =>
{
await context.Response.WriteAsync("Hello World!");
});
}

上面的代码演示了如何使用Map和MapWhen创建基于路径和条件的分支。另外,Map方法还支持层级的分支,我们参照下面的代码:

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

需要注意,使用 Map 时,将从 HttpRequest.Path 中删除匹配的Path,并针对每个请求将该线段追加到 HttpRequest.PathBase。例如对于路径/level1/level2a,当在level1App中进行处理时,它的请求路径被截断为/level2a,当在level2AApp中进行处理时,它的路径就变成/了,而相应的PathBase会变为/level1/level2a

开发中间件

看到这里,我们已经知道中间件的基本用法,是时候写一个真正意义的中间件了。

基于约定的中间件开发

在 ASP.NET Core 官网上面提供了一个简单的例子,通过中间件来设置应用的区域信息,代码如下:

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}");
});
}

通过这段代码,我们可以通过QueryString的方式设置应用的区域信息。但是这样的代码怎样复用呢?注意,中间件一定要是可复用、方便复用的。我们来改造这段代码:

public class RequestCultureMiddleware
{
private readonly RequestDelegate _next; public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
} public async Task InvokeAsync(HttpContext context)
{
//...... // Call the next delegate/middleware in the pipeline
await _next(context);
}
}

这里定义一个委托,用于执行具体的业务逻辑,然后在Configure中调用这个委托:

app.UseMiddleware<RequestCultureMiddleware>();

这样还是不太方便,不像我们使用app.UseMvc()这么方便,那么我们来添加一个扩展方法,来实现更方便的复用:

public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}

然后我们就可以这样使用中间件了:

app.UseRequestCulture();

通过委托构造中间件,应用程序在运行时创建这个中间件,并将它添加到管道中。这里需要注意的是,中间件的创建是单例的,每个中间件在应用程序生命周期内只有一个实例。那么问题来了,如果我们业务逻辑需要多个实例时,该如何操作呢?请继续往下看。

基于请求的依赖注入

通过上面的代码我们已经知道了如何编写一个中间件,如何方便的复用这个中间件。在中间件的创建过程中,容器会为我们创建一个中间件实例,并且整个应用程序生命周期中只会创建一个该中间件的实例。通常我们的程序不允许这样的注入逻辑。

其实,我们可以把中间件理解成业务逻辑的入口,真正的业务逻辑是通过Application Service层实现的,我们只需要把应用服务注入到Invoke方法中即可。

ASP.NET Core为我们提供了这种机制,允许我们按照请求进行依赖的注入,也就是每次请求创建一个服务。代码如下:

public class CustomMiddleware
{
private readonly RequestDelegate _next; public CustomMiddleware(RequestDelegate next)
{
_next = next;
} // IMyScopedService is injected into Invoke
public async Task Invoke(HttpContext httpContext, IMyScopedService svc)
{
svc.MyProperty = 1000;
await _next(httpContext);
}
}

在这段代码中,CustomMiddleware的实例仍然是单例的,但是IMyScopedService是按照请求进行注入的,每次请求都会创建IMyScopedService的实例,svc对象的生命周期是PerRequest的。

基于约定的中间件模板

这里提供一个完整的示例,可以理解为一个中间件的开发模板,方便以后使用的时候参考。整个过程分以下几步:

  • 将业务逻辑封装到ApplicationService中
  • 创建中间件代理类
  • 创建中间件扩展类
  • 使用中间件

代码如下:

namespace MiddlewareDemo
{
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks; //1.定义并实现业务逻辑
public interface IMyScopedService
{
int MyProperty { get; set; }
} public class MyScopedService : IMyScopedService
{
public int MyProperty { get; set; }
} //2.创建中间件代理类
public class CustomMiddleware
{
private readonly RequestDelegate _next; public CustomMiddleware(RequestDelegate next)
{
_next = next;
} // IMyScopedService is injected into Invoke
public async Task Invoke(HttpContext httpContext, IMyScopedService svc)
{
svc.MyProperty = 1000;
await _next(httpContext);
}
}
} //3.1 添加依赖服务注册
namespace Microsoft.Extensions.DependencyInjection
{
using MiddlewareDemo;
public static partial class CustomMiddlewareExtensions
{
/// <summary>
/// 添加服务的依赖注册
/// </summary>
public static IServiceCollection AddCustom(this IServiceCollection services)
{
return services.AddScoped<IMyScopedService, MyScopedService>();
} }
} //3.2 创建中间件扩展类
namespace Microsoft.AspNetCore.Builder
{
using MiddlewareDemo; public static partial class CustomMiddlewareExtensions
{
/// <summary>
/// 使用中间件
/// </summary>
public static IApplicationBuilder UseCustom(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomMiddleware>();
}
}
} //4. 使用中间件
public void ConfigureServices(IServiceCollection services)
{
services.AddCustom();
} public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCustom();
}

基于工厂激活的中间件

我们前面介绍的中间件开发都是基于约定的,可以让我们快速上手进行开发。同时ASP.NET Core还提供了基于工厂激活的中间件开发方式,我们可以通过实现IMiddlewareFactory、IMiddleware接口进行中间件开发。

public class FactoryActivatedMiddleware : IMiddleware
{
private readonly AppDbContext _db; public FactoryActivatedMiddleware(AppDbContext db)
{
_db = db;
} public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var keyValue = context.Request.Query["key"]; if (!string.IsNullOrWhiteSpace(keyValue))
{
_db.Add(new Request()
{
DT = DateTime.UtcNow,
MiddlewareActivation = "FactoryActivatedMiddleware",
Value = keyValue
}); await _db.SaveChangesAsync();
} await next(context);
}
}

上面这段代码演示了如何使用基于工厂激活的中间件,在使用过程中有两点需要注意:1.需要在ConfigureServices中进行服务注册;2.在UseMiddleware()方法中不支持传递参数。

参考文档

ASP.NET Core 中间件基本用法的更多相关文章

  1. Asp.net core中间件实现原理及用法解说

    简述asp.net core中间件的实现思路 原文地址:https://www.cnblogs.com/shengyu-kmust/p/11583974.html 一次http请求的过程,就是对一个R ...

  2. ASP.NET Core 中间件Diagnostics使用

    ASP.NET Core 中间件(Middleware)Diagnostics使用.对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware). Diagnost ...

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

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

  4. [转]ASP.NET Core 中间件详解及项目实战

    本文转自:http://www.cnblogs.com/savorboard/p/5586229.html 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际 ...

  5. 如何一秒钟从头构建一个 ASP.NET Core 中间件

    前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...

  6. ASP.NET Core中间件实现分布式 Session

    1. ASP.NET Core中间件详解 1.1. 中间件原理 1.1.1. 什么是中间件 1.1.2. 中间件执行过程 1.1.3. 中间件的配置 1.2. 依赖注入中间件 1.3. Cookies ...

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

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

  8. ASP.NETCore学习记录(二) —— ASP.NET Core 中间件

    ASP.NET Core 中间件 目录: 什么是中间件 ? IApplicationBuilder 使用 IApplicationBuilder 创建中间件 Run.Map 与 Use 方法 实战中间 ...

  9. (4)ASP.NET Core 中间件

    1.前言 整个HTTP Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline).而中间件(middleware)则是一种装配到请求管道以 ...

随机推荐

  1. vue-cli webpack配置cdn路径 以及 上线之后的字体文件跨域处理

    昨天搞了一下vue项目打包之后静态资源走阿里云cdn. 配置了半天,终于找到了设置的地方 config/index.js 里面设置build 下的 assetsPublicPath 打包的时候便可以添 ...

  2. matplotlib学习记录 二

    # 绘制10点到12点的每一分钟气温变化折线图 import random from matplotlib import pyplot as plt # 让matplotlib能够显示中文 plt.r ...

  3. sqlmanagementstudio2008下载地址

    http://big.wy119.com/sqlmanagementstudio2008_x86_chs.zip

  4. CodeForces - 485D Maximum Value (数学)

    题意: n个数,求出这些数中满足 ai >= aj 的 ai % aj 的最大值. 排序去重,然后对于每一个a[i], 如果找到a[i] 的一个倍数 k*a[i] (k > 1)的位置,那 ...

  5. POJ:1328-Radar Installation

    Radar Installation Time Limit: 1000MS Memory Limit: 10000K Description Assume the coasting is an inf ...

  6. UVa 1629 DP Cake slicing

    题意: 一块n×m的蛋糕上有若干个樱桃,要求切割若干次以后,每块蛋糕上有且仅有1个樱桃.求最小的切割长度. 分析: d(u, d, l, r)表示切割矩形(u, d, l, r)所需要的最小切割长度. ...

  7. clr(Windows 运行时和公共语言运行时)

    Windows 运行时   编译器使用 COM 引用计数机制来确定对象是否不再使用并可以删除. 因为从 Windows 运行时接口派生的对象实际上是 COM 对象,所以这是可行的. 在创建或复制对象时 ...

  8. 在ie9下在textbox框里面输入内容按enter键会触发按钮的事件

    问题 在ie下,如果存在有button标签,如果在textbox里面输入内容,按下enter键,则会触发第一个按钮的click事件,经过测试,在IE10以及以下的都存在这个问题 原因 浏览器默认行为不 ...

  9. xml了解

    Xml简介 ➢XML指可扩展标记语言(Extensible Markup Language) ➢XML被设计用于结构化.存储和传输数据 ➢XML是一种标记语言,很类似于HTML ➢XML没有像HTML ...

  10. Leetcode17--->Letter Combinations of a Phone Number(电话号码对应的字母的组合)

    题目: 给定一个数字字符串,返回数字所能代表的所有字母组合: 举例: Input:Digit string "23" Output: ["ad", " ...