加入收藏 | 设为首页 | 会员中心 | 我要投稿 济源站长网 (https://www.0391zz.cn/)- 数据工具、数据仓库、行业智能、CDN、运营!
当前位置: 首页 > 站长学院 > Asp教程 > 正文

ASP.NET Core处理管道的深入理解

发布时间:2020-12-11 00:33:13 所属栏目:Asp教程 来源:网络整理
导读:这篇文章主要给大家介绍了关于ASP.NET Core处理管道的深入理解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下

// 最终的处理函数 RequestDelegate app = context => { context.Output.AppendLine("End of output."); return Task.CompletedTask; }; // 定义中间件 1 Func<RequestDelegate, RequestDelegate> middleware1 = next => { return (HttpContextSample context) => { // 中间件 1 的处理内容 context.Output.AppendLine("Middleware 1 Processing."); // 调用后继的处理函数 return next(context); }; }; // 得到一个有一个处理步骤的管道 var pipeline1 = middleware1(app); // 准备一个表示当前请求的对象 var context2 = new HttpContextSample(); // 通过管道处理当前请求 pipeline1(context2); // 输出请求的处理结果 Console.WriteLine(context2.Output.ToString());

可以得到如下的输出

Middleware 1 Processing.
End of output.

继续增加第二个中间件来演示多个中间件的级联处理。

RequestDelegate app = context => { context.Output.AppendLine("End of output."); return Task.CompletedTask; }; // 定义中间件 1 Func<RequestDelegate, RequestDelegate> middleware1 = next => { return (HttpContextSample context) => { // 中间件 1 的处理内容 context.Output.AppendLine("Middleware 1 Processing."); // 调用后继的处理函数 return next(context); }; }; // 定义中间件 2 Func<RequestDelegate, RequestDelegate> middleware2 = next => { return (HttpContextSample context) => { // 中间件 2 的处理 context.Output.AppendLine("Middleware 2 Processing."); // 调用后继的处理函数 return next(context); }; }; // 构建处理管道 var step1 = middleware1(app); var pipeline2 = middleware2(step1); // 准备当前的请求对象 var context3 = new HttpContextSample(); // 处理请求 pipeline2(context3); // 输出处理结果 Console.WriteLine(context3.Output.ToString());

当前的输出

Middleware 2 Processing.
Middleware 1 Processing.
End of output.

如果我们把这些中间件保存到几个列表中,就可以通过循环来构建处理管道。下面的示例重复使用了前面定义的 app 变量。

List<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>(); _components.Add(middleware1); _components.Add(middleware2); // 构建处理管道 foreach (var component in _components) { app = component(app); } // 构建请求上下文对象 var context4 = new HttpContextSample(); // 使用处理管道处理请求 app(context4); // 输出处理结果 Console.WriteLine(context4.Output.ToString());

输出结果与上一示例完全相同

Middleware 2 Processing.
Middleware 1 Processing.
End of output.

但是,有一个问题,我们后加入到列表中的中间件 2 是先执行的,而先加入到列表中的中间件 1 是后执行的。如果希望实际的执行顺序与加入的顺序一致,只需要将这个列表再反转一下即可。

// 反转此列表 _components.Reverse(); foreach (var component in _components) { app = component(app); } var context5 = new HttpContextSample(); app(context5); Console.WriteLine(context5.Output.ToString());

输出结果如下

Middleware 1 Processing.
Middleware 2 Processing.
End of output.

现在,我们可以回到实际的 ASP.NET Core 代码中,把 ASP.NET Core 中 ApplicationBuilder 的核心代码 Build() 方法抽象之后,可以得到如下的关键代码。

注意 Build() 方法就是构建我们的请求处理管道,它返回了一个 RequestDelegate 对象,该对象实际上是一个委托对象,代表了一个处理当前请求的处理管道函数,它就是我们所谓的处理管道,以后我们将通过该委托来处理请求。

public RequestDelegate Build() { RequestDelegate app = context => { // ...... context.Response.StatusCode = StatusCodes.Status404NotFound; return Task.CompletedTask; }; foreach (var component in _components.Reverse()) { app = component(app); } return app; }

完整的 ApplicationBuilder 代码如下所示:

// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Builder { public class ApplicationBuilder : IApplicationBuilder { private const string ServerFeaturesKey = "server.Features"; private const string ApplicationServicesKey = "application.Services"; private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>(); public ApplicationBuilder(IServiceProvider serviceProvider) { Properties = new Dictionary<string, object?>(StringComparer.Ordinal); ApplicationServices = serviceProvider; } public ApplicationBuilder(IServiceProvider serviceProvider, object server) : this(serviceProvider) { SetProperty(ServerFeaturesKey, server); } private ApplicationBuilder(ApplicationBuilder builder) { Properties = new CopyOnWriteDictionary<string, object?>(builder.Properties, StringComparer.Ordinal); } public IServiceProvider ApplicationServices { get { return GetProperty<IServiceProvider>(ApplicationServicesKey)!; } set { SetProperty<IServiceProvider>(ApplicationServicesKey, value); } } public IFeatureCollection ServerFeatures { get { return GetProperty<IFeatureCollection>(ServerFeaturesKey)!; } } public IDictionary<string, object?> Properties { get; } private T? GetProperty<T>(string key) { return Properties.TryGetValue(key, out var value) ? (T)value : default(T); } private void SetProperty<T>(string key, T value) { Properties[key] = value; } public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware) { _components.Add(middleware); return this; } public IApplicationBuilder New() { return new ApplicationBuilder(this); } public RequestDelegate Build() { RequestDelegate app = context => { // If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened. // This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware. var endpoint = context.GetEndpoint(); var endpointRequestDelegate = endpoint?.RequestDelegate; if (endpointRequestDelegate != null) { var message = $"The request reached the end of the pipeline without executing the endpoint: '{endpoint!.DisplayName}'. " + $"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " + $"routing."; throw new InvalidOperationException(message); } context.Response.StatusCode = StatusCodes.Status404NotFound; return Task.CompletedTask; }; foreach (var component in _components.Reverse()) { app = component(app); } return app; } } }

见:在 GitHub 中查看 ApplicationBuilder 源码

强类型的中间件

函数形式的中间件使用比较方便,可以直接在管道定义中使用。但是,如果我们希望能够定义独立的中间件,使用强类型的类来定义会更加方便一些。

public interface IMiddleware { public System.Threading.Tasks.Task InvokeAsync ( Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Http.RequestDelegate next); }

我们定义的强类型中间件可以选择实现装个接口。

next 表示请求处理管道中的下一个中间件,处理管道会将它提供给你定义的中间件。这是将各个中间件连接起来的关键。

如果当前中间件需要将请求继续分发给后继的中间件继续处理,只需要调用这个委托对象即可。否则,应用程序针对该请求的处理到此为止。

例如,增加一个可以添加自定义响应头的中间件,如下所示:

(编辑:济源站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读