ToB企服应用市场:ToB评测及商务社交产业平台

标题: ASP.NET Core 中间件(Middleware)的使用及其源码解析(三)- 对中间件管道 [打印本页]

作者: 灌篮少年    时间: 2022-9-16 17:16
标题: ASP.NET Core 中间件(Middleware)的使用及其源码解析(三)- 对中间件管道
如果业务逻辑比较简单的话,一条主管道就够了,确实用不到分支管道。不过当业务逻辑比较复杂的时候,有时候我们可能希望根据情况的不同使用特殊的一组中间件来处理 HttpContext。这种情况下如果只用一条管道,处理起来会非常麻烦和混乱。此时就可以使用 Map/MapWhen/UseWhen 建立一个分支管道,当条件符合我们的设定时,由这个分支管道来处理 HttpContext。使用 Map/MapWhen/UseWhen 添加分支管道是很容易的,只要提供合适跳转到分支管道的判断逻辑,以及分支管道的构建方法就可以了。

一、对中间件管道进行分支

废话不多说,我们直接通过一个Demo来看一下如何对中间件管道进行分支,如下:
  1. using Microsoft.AspNetCore.Builder;
  2. using Microsoft.AspNetCore.Hosting;
  3. using Microsoft.Extensions.Configuration;
  4. using Microsoft.Extensions.DependencyInjection;
  5. using Microsoft.Extensions.Hosting;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Threading.Tasks;
  10. using NETCoreMiddleware.Middlewares;
  11. namespace NETCoreMiddleware
  12. {
  13.     public class Startup
  14.     {
  15.         public Startup(IConfiguration configuration)
  16.         {
  17.             Configuration = configuration;
  18.         }
  19.         public IConfiguration Configuration { get; }
  20.         //服务注册(往容器中添加服务)
  21.         // This method gets called by the runtime. Use this method to add services to the container.
  22.         public void ConfigureServices(IServiceCollection services)
  23.         {
  24.             services.AddControllersWithViews();
  25.         }
  26.         /// <summary>
  27.         /// 配置Http请求处理管道
  28.         /// Http请求管道模型---就是Http请求被处理的步骤
  29.         /// 所谓管道,就是拿着HttpContext,经过多个步骤的加工,生成Response,这就是管道
  30.         /// </summary>
  31.         /// <param name="app"></param>
  32.         /// <param name="env"></param>
  33.         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  34.         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  35.         {
  36.             #region 环境参数
  37.             if (env.IsDevelopment())
  38.             {
  39.                 app.UseDeveloperExceptionPage();
  40.             }
  41.             else
  42.             {
  43.                 app.UseExceptionHandler("/Home/Error");
  44.             }
  45.             #endregion 环境参数
  46.             //静态文件中间件
  47.             app.UseStaticFiles();
  48.             #region 创建管道分支
  49.             //Map管道分支
  50.             app.Map("/map1", HandleMapTest1);
  51.             app.Map("/map2", HandleMapTest2);
  52.             //MapWhen管道分支
  53.             app.MapWhen(context => context.Request.Query.ContainsKey("mapwhen"), HandleBranch);
  54.             //UseWhen管道分支
  55.             //UseWhen 与 MapWhen 不同的是,如果这个分支不发生短路或包含终端中间件,则会重新加入主管道
  56.             app.UseWhen(context => context.Request.Query.ContainsKey("usewhen"), HandleBranchAndRejoin);
  57.             #endregion 创建管道分支
  58.             #region Use中间件
  59.             //中间件1
  60.             app.Use(next =>
  61.             {
  62.                 Console.WriteLine("middleware 1");
  63.                 return async context =>
  64.                 {
  65.                     await Task.Run(() =>
  66.                     {
  67.                         Console.WriteLine("");
  68.                         Console.WriteLine("===================================Middleware===================================");
  69.                         Console.WriteLine($"This is middleware 1 Start");
  70.                     });
  71.                     await next.Invoke(context);
  72.                     await Task.Run(() =>
  73.                     {
  74.                         Console.WriteLine($"This is middleware 1 End");
  75.                         Console.WriteLine("===================================Middleware===================================");
  76.                     });
  77.                 };
  78.             });
  79.             //中间件2
  80.             app.Use(next =>
  81.             {
  82.                 Console.WriteLine("middleware 2");
  83.                 return async context =>
  84.                 {
  85.                     await Task.Run(() => Console.WriteLine($"This is middleware 2 Start"));
  86.                     await next.Invoke(context); //可通过不调用 next 参数使请求管道短路
  87.                     await Task.Run(() => Console.WriteLine($"This is middleware 2 End"));
  88.                 };
  89.             });
  90.             //中间件3
  91.             app.Use(next =>
  92.             {
  93.                 Console.WriteLine("middleware 3");
  94.                 return async context =>
  95.                 {
  96.                     await Task.Run(() => Console.WriteLine($"This is middleware 3 Start"));
  97.                     await next.Invoke(context);
  98.                     await Task.Run(() => Console.WriteLine($"This is middleware 3 End"));
  99.                 };
  100.             });
  101.             //中间件4
  102.             //Use方法的另外一个重载
  103.             app.Use(async (context, next) =>
  104.             {
  105.                 await Task.Run(() => Console.WriteLine($"This is middleware 4 Start"));
  106.                 await next();
  107.                 await Task.Run(() => Console.WriteLine($"This is middleware 4 End"));
  108.             });
  109.             #endregion Use中间件
  110.             #region UseMiddleware中间件
  111.             app.UseMiddleware<CustomMiddleware>();
  112.             #endregion UseMiddleware中间件
  113.             #region 终端中间件
  114.             //app.Use(_ => handler);
  115.             app.Run(async context =>
  116.             {
  117.                 await Task.Run(() => Console.WriteLine($"This is Run"));
  118.             });
  119.             #endregion 终端中间件
  120.             #region 最终把请求交给MVC
  121.             app.UseRouting();
  122.             app.UseAuthorization();
  123.             app.UseEndpoints(endpoints =>
  124.             {
  125.                 endpoints.MapControllerRoute(
  126.                     name: "areas",
  127.                     pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
  128.                 endpoints.MapControllerRoute(
  129.                     name: "default",
  130.                     pattern: "{controller=Home}/{action=Index}/{id?}");
  131.             });
  132.             #endregion 最终把请求交给MVC
  133.         }
  134.         #region 创建管道分支
  135.         /// <summary>
  136.         /// Map管道分支1
  137.         /// </summary>
  138.         /// <param name="app"></param>
  139.         static void HandleMapTest1(IApplicationBuilder app)
  140.         {
  141.             //中间件
  142.             app.Use(next =>
  143.             {
  144.                 Console.WriteLine("HandleMapTest1");
  145.                 return async context =>
  146.                 {
  147.                     await Task.Run(() =>
  148.                     {
  149.                         Console.WriteLine("");
  150.                         Console.WriteLine($"This is HandleMapTest1 Start");
  151.                     });
  152.                     await next.Invoke(context); //可通过不调用 next 参数使请求管道短路
  153.                     await Task.Run(() => Console.WriteLine($"This is HandleMapTest1 End"));
  154.                 };
  155.             });
  156.         }
  157.         /// <summary>
  158.         /// Map管道分支2
  159.         /// </summary>
  160.         /// <param name="app"></param>
  161.         static void HandleMapTest2(IApplicationBuilder app)
  162.         {
  163.             //中间件
  164.             app.Use(next =>
  165.             {
  166.                 Console.WriteLine("HandleMapTest2");
  167.                 return async context =>
  168.                 {
  169.                     await Task.Run(() =>
  170.                     {
  171.                         Console.WriteLine("");
  172.                         Console.WriteLine($"This is HandleMapTest2 Start");
  173.                     });
  174.                     await next.Invoke(context); //可通过不调用 next 参数使请求管道短路
  175.                     await Task.Run(() => Console.WriteLine($"This is HandleMapTest2 End"));
  176.                 };
  177.             });
  178.         }
  179.         /// <summary>
  180.         /// MapWhen管道分支
  181.         /// </summary>
  182.         /// <param name="app"></param>
  183.         static void HandleBranch(IApplicationBuilder app)
  184.         {
  185.             //中间件
  186.             app.Use(next =>
  187.             {
  188.                 Console.WriteLine("HandleBranch");
  189.                 return async context =>
  190.                 {
  191.                     await Task.Run(() =>
  192.                     {
  193.                         Console.WriteLine("");
  194.                         Console.WriteLine($"This is HandleBranch Start");
  195.                     });
  196.                     await next.Invoke(context); //可通过不调用 next 参数使请求管道短路
  197.                     await Task.Run(() => Console.WriteLine($"This is HandleBranch End"));
  198.                 };
  199.             });
  200.         }
  201.         /// <summary>
  202.         /// UseWhen管道分支
  203.         /// </summary>
  204.         /// <param name="app"></param>
  205.         static void HandleBranchAndRejoin(IApplicationBuilder app)
  206.         {
  207.             //中间件
  208.             app.Use(next =>
  209.             {
  210.                 Console.WriteLine("HandleBranchAndRejoin");
  211.                 return async context =>
  212.                 {
  213.                     await Task.Run(() =>
  214.                     {
  215.                         Console.WriteLine("");
  216.                         Console.WriteLine($"This is HandleBranchAndRejoin Start");
  217.                     });
  218.                     await next.Invoke(context); //可通过不调用 next 参数使请求管道短路
  219.                     await Task.Run(() => Console.WriteLine($"This is HandleBranchAndRejoin End"));
  220.                 };
  221.             });
  222.         }
  223.         #endregion 创建管道分支
  224.     }
  225. }
复制代码
下面我们使用命令行(CLI)方式启动我们的网站,如下所示:

启动成功后,我们来访问一下 “http://localhost:5000/map1/” ,控制台输出结果如下所示:

访问 “http://localhost:5000/map2/” ,控制台输出结果如下所示:

访问 “http://localhost:5000/home/?mapwhen=1” ,控制台输出结果如下所示:

访问 “http://localhost:5000/home/?usewhen=1” ,控制台输出结果如下所示: 

Map 扩展用作约定来创建管道分支。 Map 基于给定请求路径的匹配项来创建请求管道分支。 如果请求路径以给定路径开头,则执行分支。
MapWhen 基于给定谓词的结果创建请求管道分支。 Func 类型的任何谓词均可用于将请求映射到管道的新分支。 
UseWhen 也基于给定谓词的结果创建请求管道分支。 与 MapWhen 不同的是,如果这个分支不发生短路或包含终端中间件,则会重新加入主管道。
二、中间件管道分支源码解析

下面我们结合ASP.NET Core源码来分析下其实现原理: 
1、Map扩展原理

我们将光标移动到 Map 处按 F12 转到定义,如下所示:


可以发现它是位于 MapExtensions 扩展类中的,我们找到 MapExtensions 类的源码,如下所示:
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using Microsoft.AspNetCore.Http;
  5. using Microsoft.AspNetCore.Builder.Extensions;
  6. namespace Microsoft.AspNetCore.Builder
  7. {
  8.     /// <summary>
  9.     /// Extension methods for the <see cref="MapMiddleware"/>.
  10.     /// </summary>
  11.     public static class MapExtensions
  12.     {
  13.         /// <summary>
  14.         /// Branches the request pipeline based on matches of the given request path. If the request path starts with
  15.         /// the given path, the branch is executed.
  16.         /// </summary>
  17.         /// <param name="app">The <see cref="IApplicationBuilder"/> instance.</param>
  18.         /// <param name="pathMatch">The request path to match.</param>
  19.         /// <param name="configuration">The branch to take for positive path matches.</param>
  20.         /// <returns>The <see cref="IApplicationBuilder"/> instance.</returns>
  21.         public static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, Action<IApplicationBuilder> configuration)
  22.         {
  23.             if (app == null)
  24.             {
  25.                 throw new ArgumentNullException(nameof(app));
  26.             }
  27.             if (configuration == null)
  28.             {
  29.                 throw new ArgumentNullException(nameof(configuration));
  30.             }
  31.             if (pathMatch.HasValue && pathMatch.Value.EndsWith("/", StringComparison.Ordinal))
  32.             {
  33.                 throw new ArgumentException("The path must not end with a '/'", nameof(pathMatch));
  34.             }
  35.             // create branch
  36.             var branchBuilder = app.New();
  37.             configuration(branchBuilder);
  38.             var branch = branchBuilder.Build();
  39.             var options = new MapOptions
  40.             {
  41.                 Branch = branch,
  42.                 PathMatch = pathMatch,
  43.             };
  44.             return app.Use(next => new MapMiddleware(next, options).Invoke);
  45.         }
  46.     }
  47. }
复制代码
其中 ApplicationBuilder 类的源码,如下所示: 
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Threading.Tasks;
  7. using Microsoft.AspNetCore.Http;
  8. using Microsoft.AspNetCore.Http.Features;
  9. using Microsoft.Extensions.Internal;
  10. namespace Microsoft.AspNetCore.Builder
  11. {
  12.     public class ApplicationBuilder : IApplicationBuilder
  13.     {
  14.         private const string ServerFeaturesKey = "server.Features";
  15.         private const string ApplicationServicesKey = "application.Services";
  16.         private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();
  17.         public ApplicationBuilder(IServiceProvider serviceProvider)
  18.         {
  19.             Properties = new Dictionary<string, object>(StringComparer.Ordinal);
  20.             ApplicationServices = serviceProvider;
  21.         }
  22.         public ApplicationBuilder(IServiceProvider serviceProvider, object server)
  23.             : this(serviceProvider)
  24.         {
  25.             SetProperty(ServerFeaturesKey, server);
  26.         }
  27.         private ApplicationBuilder(ApplicationBuilder builder)
  28.         {
  29.             Properties = new CopyOnWriteDictionary<string, object>(builder.Properties, StringComparer.Ordinal);
  30.         }
  31.         public IServiceProvider ApplicationServices
  32.         {
  33.             get
  34.             {
  35.                 return GetProperty<IServiceProvider>(ApplicationServicesKey);
  36.             }
  37.             set
  38.             {
  39.                 SetProperty<IServiceProvider>(ApplicationServicesKey, value);
  40.             }
  41.         }
  42.         public IFeatureCollection ServerFeatures
  43.         {
  44.             get
  45.             {
  46.                 return GetProperty<IFeatureCollection>(ServerFeaturesKey);
  47.             }
  48.         }
  49.         public IDictionary<string, object> Properties { get; }
  50.         private T GetProperty<T>(string key)
  51.         {
  52.             object value;
  53.             return Properties.TryGetValue(key, out value) ? (T)value : default(T);
  54.         }
  55.         private void SetProperty<T>(string key, T value)
  56.         {
  57.             Properties[key] = value;
  58.         }
  59.         public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
  60.         {
  61.             _components.Add(middleware);
  62.             return this;
  63.         }
  64.         public IApplicationBuilder New()
  65.         {
  66.             return new ApplicationBuilder(this);
  67.         }
  68.         public RequestDelegate Build()
  69.         {
  70.             RequestDelegate app = context =>
  71.             {
  72.                 // If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened.
  73.                 // This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware.
  74.                 var endpoint = context.GetEndpoint();
  75.                 var endpointRequestDelegate = endpoint?.RequestDelegate;
  76.                 if (endpointRequestDelegate != null)
  77.                 {
  78.                     var message =
  79.                         $"The request reached the end of the pipeline without executing the endpoint: '{endpoint.DisplayName}'. " +
  80.                         $"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " +
  81.                         $"routing.";
  82.                     throw new InvalidOperationException(message);
  83.                 }
  84.                 context.Response.StatusCode = 404;
  85.                 return Task.CompletedTask;
  86.             };
  87.             foreach (var component in _components.Reverse())
  88.             {
  89.                 app = component(app);
  90.             }
  91.             return app;
  92.         }
  93.     }
  94. }
复制代码
Microsoft.AspNetCore.Builder.ApplicationBuilder类源码其中 MapMiddleware 类的源码,如下所示: 
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Http;
  6. namespace Microsoft.AspNetCore.Builder.Extensions
  7. {
  8.     /// <summary>
  9.     /// Represents a middleware that maps a request path to a sub-request pipeline.
  10.     /// </summary>
  11.     public class MapMiddleware
  12.     {
  13.         private readonly RequestDelegate _next;
  14.         private readonly MapOptions _options;
  15.         /// <summary>
  16.         /// Creates a new instance of <see cref="MapMiddleware"/>.
  17.         /// </summary>
  18.         /// <param name="next">The delegate representing the next middleware in the request pipeline.</param>
  19.         /// <param name="options">The middleware options.</param>
  20.         public MapMiddleware(RequestDelegate next, MapOptions options)
  21.         {
  22.             if (next == null)
  23.             {
  24.                 throw new ArgumentNullException(nameof(next));
  25.             }
  26.             if (options == null)
  27.             {
  28.                 throw new ArgumentNullException(nameof(options));
  29.             }
  30.             _next = next;
  31.             _options = options;
  32.         }
  33.         /// <summary>
  34.         /// Executes the middleware.
  35.         /// </summary>
  36.         /// <param name="context">The <see cref="HttpContext"/> for the current request.</param>
  37.         /// <returns>A task that represents the execution of this middleware.</returns>
  38.         public async Task Invoke(HttpContext context)
  39.         {
  40.             if (context == null)
  41.             {
  42.                 throw new ArgumentNullException(nameof(context));
  43.             }
  44.             PathString matchedPath;
  45.             PathString remainingPath;
  46.             if (context.Request.Path.StartsWithSegments(_options.PathMatch, out matchedPath, out remainingPath))
  47.             {
  48.                 // Update the path
  49.                 var path = context.Request.Path;
  50.                 var pathBase = context.Request.PathBase;
  51.                 context.Request.PathBase = pathBase.Add(matchedPath);
  52.                 context.Request.Path = remainingPath;
  53.                 try
  54.                 {
  55.                     await _options.Branch(context);
  56.                 }
  57.                 finally
  58.                 {
  59.                     context.Request.PathBase = pathBase;
  60.                     context.Request.Path = path;
  61.                 }
  62.             }
  63.             else
  64.             {
  65.                 await _next(context);
  66.             }
  67.         }
  68.     }
  69. }
复制代码
在前两篇文章中我们已经介绍过,中间件的注册和管道的构建都是通过 ApplicationBuilder 进行的。因此要构建一个分支管道,需要一个新的 ApplicationBuilder ,并用它来注册中间件,构建管道。为了在分支管道中也能够共享我们在当前 ApplicationBuilder 中注册的服务(或者说共享依赖注入容器,当然共享的并不止这些),在创建新的 ApplicationBuilder 时并不是直接 new 一个全新的,而是调用当前 ApplicationBuilder 的 New 方法在当前的基础上创建新的,共享了当前 ApplicationBuilder 的 Properties(其中包含了依赖注入容器)。 
在使用 Map 注册中间件时我们会传入一个 Action 参数,它的作用就是当我们创建了新的 ApplicationBuilder 后,使用这个方法对其进行各种设置,最重要的就是在新的 ApplicationBuilder 上注册分支管道的中间件。配置完成后调用分支 ApplicationBuilder 的 Build 方法构建管道,并把第一个中间件保存下来作为分支管道的入口。
在使用 Map 注册中间件时传入了一个 PathString 参数,PathString 对象我们可以简单地认为是 string 。它用于记录 HttpContext.HttpRequest.Path 中要匹配的区段(Segment)。这个字符串参数结尾不能是“/”。如果匹配成功则进入分支管道,匹配失则败继续当前管道。
新构建的管道和用于匹配的字符串保存为 MapOptions 对象,保存了 Map 规则和分支管道的入口。之后构建 MapMiddleware 对象,并把它的 Invoke 方法包装为 RequestDelegate ,使用当前 ApplicationBuilder 的 Use 方法注册中间件。
2、MapWhen扩展原理

我们将光标移动到 MapWhen 处按 F12 转到定义,如下所示:


可以发现它是位于 MapWhenExtensions 扩展类中的,我们找到 MapWhenExtensions 类的源码,如下所示: 
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using Microsoft.AspNetCore.Http;
  5. using Microsoft.AspNetCore.Builder.Extensions;
  6. namespace Microsoft.AspNetCore.Builder
  7. {
  8.     using Predicate = Func<HttpContext, bool>;
  9.     /// <summary>
  10.     /// Extension methods for the <see cref="MapWhenMiddleware"/>.
  11.     /// </summary>
  12.     public static class MapWhenExtensions
  13.     {
  14.         /// <summary>
  15.         /// Branches the request pipeline based on the result of the given predicate.
  16.         /// </summary>
  17.         /// <param name="app"></param>
  18.         /// <param name="predicate">Invoked with the request environment to determine if the branch should be taken</param>
  19.         /// <param name="configuration">Configures a branch to take</param>
  20.         /// <returns></returns>
  21.         public static IApplicationBuilder MapWhen(this IApplicationBuilder app, Predicate predicate, Action<IApplicationBuilder> configuration)
  22.         {
  23.             if (app == null)
  24.             {
  25.                 throw new ArgumentNullException(nameof(app));
  26.             }
  27.             if (predicate == null)
  28.             {
  29.                 throw new ArgumentNullException(nameof(predicate));
  30.             }
  31.             if (configuration == null)
  32.             {
  33.                 throw new ArgumentNullException(nameof(configuration));
  34.             }
  35.             // create branch
  36.             var branchBuilder = app.New();
  37.             configuration(branchBuilder);
  38.             var branch = branchBuilder.Build();
  39.             // put middleware in pipeline
  40.             var options = new MapWhenOptions
  41.             {
  42.                 Predicate = predicate,
  43.                 Branch = branch,
  44.             };
  45.             return app.Use(next => new MapWhenMiddleware(next, options).Invoke);
  46.         }
  47.     }
  48. }
复制代码
其中 MapWhenMiddleware 类的源码,如下所示: 
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Http;
  6. namespace Microsoft.AspNetCore.Builder.Extensions
  7. {
  8.     /// <summary>
  9.     /// Represents a middleware that runs a sub-request pipeline when a given predicate is matched.
  10.     /// </summary>
  11.     public class MapWhenMiddleware
  12.     {
  13.         private readonly RequestDelegate _next;
  14.         private readonly MapWhenOptions _options;
  15.         /// <summary>
  16.         /// Creates a new instance of <see cref="MapWhenMiddleware"/>.
  17.         /// </summary>
  18.         /// <param name="next">The delegate representing the next middleware in the request pipeline.</param>
  19.         /// <param name="options">The middleware options.</param>
  20.         public MapWhenMiddleware(RequestDelegate next, MapWhenOptions options)
  21.         {
  22.             if (next == null)
  23.             {
  24.                 throw new ArgumentNullException(nameof(next));
  25.             }
  26.             if (options == null)
  27.             {
  28.                 throw new ArgumentNullException(nameof(options));
  29.             }
  30.             _next = next;
  31.             _options = options;
  32.         }
  33.         /// <summary>
  34.         /// Executes the middleware.
  35.         /// </summary>
  36.         /// <param name="context">The <see cref="HttpContext"/> for the current request.</param>
  37.         /// <returns>A task that represents the execution of this middleware.</returns>
  38.         public async Task Invoke(HttpContext context)
  39.         {
  40.             if (context == null)
  41.             {
  42.                 throw new ArgumentNullException(nameof(context));
  43.             }
  44.             if (_options.Predicate(context))
  45.             {
  46.                 await _options.Branch(context);
  47.             }
  48.             else
  49.             {
  50.                 await _next(context);
  51.             }
  52.         }
  53.     }
  54. }
复制代码
Map 主要通过 URL 中的 Path 来判断是否需要进入分支管道,但有时候我们很可能会有别的需求,例如我想对所有 Method 为 DELETE 的请求用特殊管道处理,这时候就需要用 MapWhen 了。MapWhen 是一种通用的 Map,可以由使用者来决定什么时候进入分支管道什么时候不进入。可以说 Map 是 MapWhen 的一种情况,因为这种情况太常见了,所以官方实现了一个。这样看来 MapWhen 就很简单了,在 Map 中我们传入参数 PathString 来进行 HttpRequest.Path 的匹配,而在 MapWhen 中我们传入 Func 参数,由我们自行指定,当返回 true 时进入分支管道,返回 false 则继续当前管道。
3、UseWhen扩展原理

我们将光标移动到 UseWhen 处按 F12 转到定义,如下所示: 


可以发现它是位于 UseWhenExtensions 扩展类中的,我们找到 UseWhenExtensions 类的源码,如下所示: 
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using Microsoft.AspNetCore.Http;
  5. namespace Microsoft.AspNetCore.Builder
  6. {
  7.     using Predicate = Func<HttpContext, bool>;
  8.     /// <summary>
  9.     /// Extension methods for <see cref="IApplicationBuilder"/>.
  10.     /// </summary>
  11.     public static class UseWhenExtensions
  12.     {
  13.         /// <summary>
  14.         /// Conditionally creates a branch in the request pipeline that is rejoined to the main pipeline.
  15.         /// </summary>
  16.         /// <param name="app"></param>
  17.         /// <param name="predicate">Invoked with the request environment to determine if the branch should be taken</param>
  18.         /// <param name="configuration">Configures a branch to take</param>
  19.         /// <returns></returns>
  20.         public static IApplicationBuilder UseWhen(this IApplicationBuilder app, Predicate predicate, Action<IApplicationBuilder> configuration)
  21.         {
  22.             if (app == null)
  23.             {
  24.                 throw new ArgumentNullException(nameof(app));
  25.             }
  26.             if (predicate == null)
  27.             {
  28.                 throw new ArgumentNullException(nameof(predicate));
  29.             }
  30.             if (configuration == null)
  31.             {
  32.                 throw new ArgumentNullException(nameof(configuration));
  33.             }
  34.             // Create and configure the branch builder right away; otherwise,
  35.             // we would end up running our branch after all the components
  36.             // that were subsequently added to the main builder.
  37.             var branchBuilder = app.New();
  38.             configuration(branchBuilder);
  39.             return app.Use(main =>
  40.             {
  41.                 // This is called only when the main application builder
  42.                 // is built, not per request.
  43.                 branchBuilder.Run(main);
  44.                 var branch = branchBuilder.Build();
  45.                 return context =>
  46.                 {
  47.                     if (predicate(context))
  48.                     {
  49.                         return branch(context);
  50.                     }
  51.                     else
  52.                     {
  53.                         return main(context);
  54.                     }
  55.                 };
  56.             });
  57.         }
  58.     }
  59. }
复制代码
UseWhen 也基于给定谓词的结果创建请求管道分支。 与 MapWhen 不同的是,如果这个分支不发生短路或包含终端中间件,则会重新加入主管道。
 
本文部分内容参考博文:https://www.cnblogs.com/durow/p/5752055.html
更多关于ASP.NET Core 中间件的相关知识可参考微软官方文档: https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-6.0
至此本文就全部介绍完了,如果觉得对您有所启发请记得点个赞哦!!!
 
Demo源码:
  1. 链接:https://pan.baidu.com/s/18I66dBmKZUpfPCNn85HI2g
  2. 提取码:2xcj
复制代码
此文由博主精心撰写转载请保留此原文链接:https://www.cnblogs.com/xyh9039/p/16492284.html
版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4