个人网站 备案 攻略,2017网站建设,硬件开发平台有哪些,wordpress加字体前言Asp.NetCore中的请求管道是通过一系列的中间件组成的#xff0c;使得请求会根据需求进行对应的过滤和加工处理。在平时开发中会时常引用别人定义好的中间件#xff0c;只需简单进行app.Usexxx就能完成中间件的注册#xff0c;但是对于一些定制化需求还得自己进行处理和封… 前言Asp.NetCore中的请求管道是通过一系列的中间件组成的使得请求会根据需求进行对应的过滤和加工处理。在平时开发中会时常引用别人定义好的中间件只需简单进行app.Usexxx就能完成中间件的注册但是对于一些定制化需求还得自己进行处理和封装以下说说中间件的注册应用和自定义中间件正文在上一小节中有简单提到当注册第三方封装的中间件时其实本质还是调用了IApplicationBuilder的Use方法而在开发过程中会使用以下三种方式进行中间件的注册Use通过Use的方式注册中间件可以控制是否将请求传递到下一个中间件Run通过Run的方式注册中间件一般用于断路或请求管道末尾即不会将请求传递下去Map/MapWhen请求管道中增加分支条件满足之后就由分支管道进行处理而不会切换回主管道Map用于请求路径匹配而MapWhen可以有更多的条件进行过滤UseMiddleWare : 一般用于注册自定义封装的中间件内部其实是使用Use的方式进行中间件注册相信都知道我的套路了光说不练假把式来一个Asp.NetCore API项目进行以上几种中间件注册方式演示图中代码部分将原先默认注册的中间件删除了用Use和Run的方式分别注册了两个中间件(这里只是简单的显示文字里面可以根据需求添加相关逻辑)其中用Use注册的方式在上一节中已经提及到直接将中间件添加链表中这里就不再赘述了对于使用Run方式注册中间小伙伴们肯定不甘心止于此吧所以这里直接看Run是如何实现namespace Microsoft.AspNetCore.Builder
{public static class RunExtensions{// 也是一个扩展方法但参数就是一个委托public static void Run(this IApplicationBuilder app, RequestDelegate handler){// 参数校验如果null就抛出异常if (app null){throw new ArgumentNullException(nameof(app));}// 传入的委托校验如果null也是抛出异常if (handler null){throw new ArgumentNullException(nameof(handler));}// 这里其实只有一个 RequestDelegate执行逻辑并没有传递功能// 本质也是使用方法Useapp.Use(_ handler);}}
}
通过代码可知用Run方式只是将处理逻辑RequestDelegate传入并没有传递的逻辑所以Run注册的中间件就会形成断路导致后面的中间件不能再执行了使用Map和MapWhen注册的方式其实是给管道开一个分支就像高速公路一样有匝道到了对应出口就进匝道了就不能倒车回来了(倒回来扣你12分);同样请求管道也是当条件满足时请求就走Map对应的分支管道就不能重新返回主管道了代码走一波在注册中间件的地方增加Map的使用Configure全部代码如下public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{// 使用Use注册中间 app.Use(async (context, next) {await context.Response.WriteAsync(Hello Use1\r\n);// 将请求传递到下一个中间件await next();await context.Response.WriteAsync(Hello Use1 Response\r\n);});// 使用Use注册中间 参数类型不一样app.Use(requestDelegate {return async (context) {await context.Response.WriteAsync(Hello Use2\r\n);// 将请求传递到下一个中间件await requestDelegate(context);await context.Response.WriteAsync(Hello Use2 Response\r\n);};});// 分支管道只有匹配到路径才走分支管道app.Map(/Hello, builder {builder.Use(async (context, next) {await context.Response.WriteAsync(Hello MapUse\r\n);// 将请求传递到分支管道的下一个中间件await next();await context.Response.WriteAsync(Hello MapUse Response\r\n);});// 注册分支管道中间件builder.Run(async context {await context.Response.WriteAsync(Hello MapRun1~~~\r\n);});// 注册分支管道中间件builder.Run(async context {await context.Response.WriteAsync(Hello MapRun2~~~\r\n);});});// 使用Run app.Run(async context {await context.Response.WriteAsync(Hello Run~~~\r\n);});//使用Run注册app.Run(async context {await context.Response.WriteAsync(Hello Code综艺圈~~~\r\n);});
}
执行看效果Map方式注册的分支管道只有路径匹配了才走否则都会走主管道仔细的小伙伴肯定会说那是在分支管道上用了Run注册中间件了形成了断路所以导致不能执行主管道剩下的中间件好那我们稍微改改代码这样运行访问分支管道时会报错因为分支管道中没有下一个中间件了还调用下一个中间件那肯定有问题改了改如下运行进入匝道还想倒回来12分不要了吗哈哈哈MapWhen注册的分支管道逻辑和Map差不多类似只是匹配的条件更加灵活而已可以根据自己需求进行调节匹配如下看到这小伙伴应该都知道接下来肯定不会放过Map/MapWhen的实现Mappublic static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, ActionIApplicationBuilder configuration)
{// 进行参数校验 IApplicationBuilder对象不能为空if (app null){throw new ArgumentNullException(nameof(app));}// 进行参数校验 传进的委托对象不能为空if (configuration null){throw new ArgumentNullException(nameof(configuration));}// 匹配的路径末尾不能有/,否则就抛异常if (pathMatch.HasValue pathMatch.Value.EndsWith(/, StringComparison.Ordinal)){throw new ArgumentException(The path must not end with a /, nameof(pathMatch));}// 克隆一个IApplicationBuilder共用之前的属性这里其实创建了分支管道var branchBuilder app.New();// 将创建出来的branchBuilder进行相关配置configuration(branchBuilder);// 构造出分支管道var branch branchBuilder.Build();// 将构造出来的管道和匹配路径进行封装var options new MapOptions{Branch branch,PathMatch pathMatch,};// 注册中间件return app.Use(next new MapMiddleware(next, options).Invoke);
}// MapMiddleware 的Invoke方法及如何进入分支管道处理的
public async Task Invoke(HttpContext context)
{// 参数判断if (context null){throw new ArgumentNullException(nameof(context));}PathString matchedPath;PathString remainingPath;// 判断是否匹配路径如果匹配上就进入分支if (context.Request.Path.StartsWithSegments(_options.PathMatch, out matchedPath, out remainingPath)){// 更新请求地址var path context.Request.Path;var pathBase context.Request.PathBase;context.Request.PathBase pathBase.Add(matchedPath);context.Request.Path remainingPath;try{// 进入分支管道await _options.Branch(context);}finally{// 恢复原先请求地址回到主管道之后并没有进行主管道也下一个中间件的传递所以主管道后续不在执行context.Request.PathBase pathBase;context.Request.Path path;}}else{// 匹配不到路径就继续主管道执行await _next(context);}
}
MapWhen其实和Map差不多只是传入的匹配规则不一样比较灵活public static IApplicationBuilder MapWhen(this IApplicationBuilder app, Predicate predicate, ActionIApplicationBuilder configuration)
{if (app null){throw new ArgumentNullException(nameof(app));}if (predicate null){throw new ArgumentNullException(nameof(predicate));}if (configuration null){throw new ArgumentNullException(nameof(configuration));}// 构建分支管道和Map一致var branchBuilder app.New();configuration(branchBuilder);var branch branchBuilder.Build();// 封装匹配规则var options new MapWhenOptions{Predicate predicate,Branch branch,};// 注册中间件return app.Use(next new MapWhenMiddleware(next, options).Invoke);
}
// MapWhenMiddleware 的Invoke方法
public async Task Invoke(HttpContext context)
{// 参数校验if (context null){throw new ArgumentNullException(nameof(context));}// 判断是否匹配规则如果匹配就进入分支管道if (_options.Predicate(context)){await _options.Branch(context);}else{// 没有匹配就继续执行主管道await _next(context);}
}现在是不是清晰明了多了不懵了吧还没完呢继续往下上面注册中间件的方式是不是有点不那么好看当中间件多了时候可读性很是头疼维护性也得花点功夫所以微软肯定想到这了提供了类的方式进行中间件的封装(但是要按照约定来)从而可以像使用第三方中间件那样简单如下使用及运行是不是自定义也没想象中那么难其中注册封装的中间件时在扩展方法中使用了app.UseMiddlewareT()进行注册这个上一节中提到过就是那段在上一节中有点嫌早的代码这里就拷过来了(偷个懒)// 看着调用的方法
public static IApplicationBuilder UseMiddlewareTMiddleware(this IApplicationBuilder app, params object[] args)
{// 内部调用了以下方法return app.UseMiddleware(typeof(TMiddleware), args);
}
// 其实这里是对自定义中间件的注册这里可以不用太深入了解
public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args)
{if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo())){// IMiddleware doesnt support passing args directly since its// activated from the containerif (args.Length 0){throw new NotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));}return UseMiddlewareInterface(app, middleware);}// 取得容器var applicationServices app.ApplicationServices;// 反编译进行包装成注册中间件的样子(FuncReuqestDelegate,RequestDelegate)但可以看到本质使用IApplicationBuilder中Use方法return app.Use(next {// 获取指定类型中的方法列表var methods middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);// 找出名字是Invoke或是InvokeAsync的方法var invokeMethods methods.Where(m string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)|| string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal)).ToArray();// 如果有多个方法 就抛出异常这里保证方法的唯一if (invokeMethods.Length 1){throw new InvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName, InvokeAsyncMethodName));}// 如果没有找到也就抛出异常if (invokeMethods.Length 0){throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName, InvokeAsyncMethodName, middleware));}// 取得唯一的方法Invoke或是InvokeAsync方法var methodInfo invokeMethods[0];// 判断类型是否返回Task,如果不是就抛出异常要求返回Task的目的是为了后续包装RequestDelegateif (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType)){throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, InvokeAsyncMethodName, nameof(Task)));}// 判断方法的参数参数的第一个参数必须是HttpContext类型var parameters methodInfo.GetParameters();if (parameters.Length 0 || parameters[0].ParameterType ! typeof(HttpContext)){throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, InvokeAsyncMethodName, nameof(HttpContext)));}// 开始构造RequestDelegate对象var ctorArgs new object[args.Length 1];ctorArgs[0] next;Array.Copy(args, 0, ctorArgs, 1, args.Length);// 这里找到参数匹配最多的构造函数进行实例创建var instance ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);// 如果参数只有一个HttpContext 就包装成一个RequestDelegate返回if (parameters.Length 1){return (RequestDelegate)methodInfo.CreateDelegate(typeof(RequestDelegate), instance);}// 如果参数有多个的情况就单独处理这里不详细进去了var factory Compileobject(methodInfo, parameters);return context {var serviceProvider context.RequestServices ?? applicationServices;if (serviceProvider null){throw new InvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));}return factory(instance, context, serviceProvider);};});
}
可以看出框架将我们封装的中间件类进行了反射获取对应的方法和属性然后封装成中间件(FuncRequestDelegate,RequestDelegate)的样子从而是得编码更加方便中间件更容易分类管理了通过以上代码注释也能看出在封装中间件的时候对应的约定哈哈哈是不是得重新看一遍代码(如果这样目标达到了)对了框架提供了IMiddleware了接口实现中间件的时候可以实现但是约定还是一个不能少总结我去不能熬了再熬明天起不来跑步了这篇内容有点多之所以没分开感觉关联性比较强一口气看下来比较合适下一节说说文件相关的点---------------------------------------------------CSDNCode综艺圈知乎Code综艺圈掘金Code综艺圈博客园Code综艺圈bilibiliCode综艺圈---------------------------------------------------一个被程序搞丑的帅小伙关注Code综艺圈识别关注跟我一起学~~~撸文不易莫要白瞟三连走起~~~~