ASP.NET Core 中间件基本用法
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()方法中不支持传递参数。
参考文档
- https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/
- https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/write
- https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/extensibility
ASP.NET Core 中间件基本用法的更多相关文章
- Asp.net core中间件实现原理及用法解说
简述asp.net core中间件的实现思路 原文地址:https://www.cnblogs.com/shengyu-kmust/p/11583974.html 一次http请求的过程,就是对一个R ...
- 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 方法 实战中间 ...
- (4)ASP.NET Core 中间件
1.前言 整个HTTP Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline).而中间件(middleware)则是一种装配到请求管道以 ...
随机推荐
- destoon 给超级管理员系统权限(管理员管理,日志管理等)
destoon 后台某些系统权限除了网站创始人之外其他超管事没有权限的,现需要给其他超级管理员添加普通管理员的权限. 1.首先 admin/global.func.php admin_check函 ...
- 无需上传附件到服务器,Servlet读取Excel(二)
package com.str; import java.io.File;import java.io.FileInputStream;import java.io.IOException; impo ...
- matplotlib学习记录 一
from matplotlib import pyplot as plt # 先实例一个图片,传入图片参数,10宽,5高,分辨率为80 image = plt.figure(figsize=(10,5 ...
- 重新造轮子之静态链接1(Static linking)
最近学习计算机病毒学的过程中,又讲到了静态链接的问题,联想到了之前保健哥在信息安全的课堂上向我们展示了一个没有main()函数的C程序到底应该如何编写.个人觉得这个小实验对于加深静态链接的过程的理解也 ...
- HDU 4866 Shooting 扫描线 + 主席树
题意: 在二维平面的第一象限有\(n(1 \leq n \leq 10^5)\)条平行于\(x\)轴的线段,接下来有\(m\)次射击\(x \, a \, b \, c\). 每次射击会获得一定的分数 ...
- 大咖分享 | 一文解锁首届云创大会干货——上篇(文末附演讲ppt文件免费下载)
日,第一届网易云创大会在杭州国际博览中心举办,本次大会由杭州滨江区政府和网易主办,杭州市两创示范工作领导小组办公室协办,网易云承办,以"商业匠心.技术创新"为主题,致力于打通技术创 ...
- 链表中倒数第k个结点 【微软面试100题 第十三题】
题目要求: 输入一个链表,输出该链表中倒数第k个结点.链表的倒数第0个结点为链表的尾指针. 参考资料:剑指offer第15题 题目分析: 1.两个指针,第一个先走k步,然后两个指针同时走,直到第一个走 ...
- python + selenium + unittest 自动化测试框架 -- 入门篇
. 预置条件: 1. python已安装 2. pycharm已安装 3. selenium已安装 4. chrome.driver 驱动已下载 二.工程建立 1. New Project:建立自己的 ...
- jquery源码区块
- python闭包函数、装饰器
闭包函数的传值方式: 方式1:通过参数传值 def func(x): print(x)func(1) 方式2:闭包函数传值 def outter(x): def inner(): print(x) r ...