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中,关于CKEditor使用

    官方文档 语言配置 代码如下 ClassicEditor .create( document.querySelector( '#editor' ), { language: 'de' // 配置语言 ...

  2. python之绝对导入和相对导入

    绝对导入 import sys, os BASE_DIR = os.path.dirname(os.path.dirname(__file__)) sys.path.append(BASE_DIR) ...

  3. #2 create and populate a database && realistic and practical applications

    The Chapter3 & Chapter4 of this book tells you how to create a realistic app on the web through ...

  4. UOJ 152 汉诺塔 分治

    题目链接 题意: 有三根编号为\((1, \, 2, \, 3)\)的柱子,然后第一根柱子上有编号为\(1 \sim n(n \leq 10000)\)的盘子,从上到下第\(i\)个盘子的编号是\(A ...

  5. python socket相关

    套接字的工作流程(基于TCP和 UDP两个协议) TCP和UDP对比 TCP(Transmission Control Protocol)可靠的.面向连接的协议(eg:打电话).传输效率低全双工通信( ...

  6. 贪心 - [POI2006]ORK-Ploughing

    [POI2006]ORK-Ploughing 描述 Byteasar 想耕种他那块矩形的田,他每次能耕种矩形的一边(上下左右都行),在他每次耕完后,剩下的田也一定是矩形,每块小区域边长为 1,耕地的长 ...

  7. Linux中 find 常见用法示例

    Linux中find常见用法示例 #find path -option [ -print ] [ -exec -ok command ] {} \; #-print 将查找到的文件输出到标准输出 #- ...

  8. Selenium WebDriver-操作键盘事件

    # 注意: !!!操作操作系统的按键,需要先装pywin32,然后通过交互模式import win32api和import win32con判断是否安装成功,需要重启下cmd进入交互模式# 下载链接: ...

  9. TensorFlow TFRecord封装不定长的序列数据(文本)

    TensorFlow TFRecord封装不定长的序列数据(文本) 在实验室环境中,通常数据都是一次性导入内存的,然后使用手工写的数据mini-batch函数来切分数据,但是这样的做法在海量数据下显得 ...

  10. LinearLayout 滚动条

    比如文件列表, 显示不下,需要出滚动条:外面加一个scrollview 直接上代码: <?xml version="1.0" encoding="utf-8&quo ...