asp.net core 中间件详解及项目实战

前言

在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际使用的,比较贴合实际应用,算是对中间件的一个深入使用了,不是简单的Hello World,如果你觉得本篇文章对你有用的话,不妨点个【推荐】。

目录

  • 中间件(Middleware)的作用

  • 中间件的运行方式

  • 中间件(Middleware)和过滤器(Filter)的区别

  • 什么情况我们需要中间件

  • 怎么样自定义自己的中间件

中间件(Middleware)的作用

我们知道,任何的一个web框架都是把http请求封装成一个管道,每一次的请求都是经过管道的一系列操作,最终到达我们写的代码中。那么中间件就是在应用程序管道中的一个组件,用来拦截请求过程进行一些其他处理和响应。中间件可以有很多个,每一个中间件都可以对管道中的请求进行拦截,它可以决定是否将请求转移给下一个中间件。

asp.net core 提供了IApplicationBuilder接口来让把中间件注册到asp.net的管道请求当中去,中间件是一个典型的AOP应用。 下面是一个微软官方的一个中间件管道请求图:

可以看到,每一个中间件都都可以在请求之前和之后进行操作。请求处理完成之后传递给下一个请求。

中间件的运行方式

默认情况下,中间件的执行顺序根据Startup.cs文件中,在public void Configure(IApplicationBuilder app){} 方法中注册的先后顺序执行。
大概有3种方式可以在管道中注册"中间件"

  1. app.Use()IApplicationBuilder接口原生提供,注册等都用它。

  2. app.Run() ,是一个扩展方法,它需要一个RequestDelegate委托,里面包含了Http的上下文信息,没有next参数,因为它总是在管道最后一步执行。

  3. app.Map(),也是一个扩展方法,类似于MVC的路由,用途一般是一些特殊请求路径的处理。如:www.example.com/token 等。

上面的Run,Map内部也是调用的Use,算是对IApplicationBuilder接口扩充,如果你觉得名字都不够准确,那么下面这个扩展方法就是正宗的注册中间件的了,也是功能最强大的。
app.UseMiddleware<>(),没错,就是这个了。 为什么说功能强大呢?是因为它不但提供了注册中间件的功能,还提供了依赖注入(DI)的功能,以后大部分情况就用它了。

中间件(Middleware)和过滤器(Filter)的区别

熟悉MVC框架的同学应该知道,MVC也提供了5大过滤器供我们用来处理请求前后需要执行的代码。分别是AuthenticationFilter,AuthorizationFilter,ActionFilter,ExceptionFilter,ResultFilter

根据描述,可以看出中间件和过滤器的功能类似,那么他们有什么区别?为什么又要搞一个中间件呢?
其实,过滤器和中间件他们的关注点是不一样的,也就是说职责不一样,干的事情就不一样。

举个栗子,中间件像是埃辛诺斯战刃,过滤器像是巨龙之怒,泰蕾苟萨的寄魂杖 ,你一个战士拿着巨龙之怒,泰蕾苟萨的寄魂杖去战场杀人,虽然都有伤害,但是你拿着法杖伤害低不说,还减属性啊。

同作为两个AOP利器,过滤器更贴合业务,它关注于应用程序本身,比如你看ActionFilterResultFilter,它都直接和你的Action,ActionResult交互了,是不是离你很近的感觉,那我有一些比如对我的输出结果进行格式化啦,对我的请求的ViewModel进行数据验证啦,肯定就是用Filter无疑了。它是MVC的一部分,它可以拦截到你Action上下文的一些信息,而中间件是没有这个能力的。

什么情况我们需要中间件

那么,何时使用中间件呢?我的理解是在我们的应用程序当中和业务关系不大的一些需要在管道中做的事情可以使用,比如身份验证,Session存储,日志记录等。其实我们的 asp.net core项目中本身已经包含了很多个中间件。

举例,我们在新建一个 asp.net core应用程序的时候,默认生成的模板当中

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory){app.UseDeveloperExceptionPage();app.UseStaticFiles();loggerFactory.AddConsole();app.UseMvc(routes =>{routes.MapRoute(name: "default",            template: "{controller=Home}/{action=Index}/{id?}");});
}

懒得去下载源码了,我们使用Reflector去查看源码:

//扩展方法`app.UseDeveloperExceptionPage();`public static class DeveloperExceptionPageExtensions{    // Methodspublic static IApplicationBuilder UseDeveloperExceptionPage(this IApplicationBuilder app)    {        if (app == null){            throw new ArgumentNullException("app");}        return UseMiddlewareExtensions.UseMiddleware<DeveloperExceptionPageMiddleware>(app, Array.Empty<object>());}
}
//扩展方法`app.UseStaticFiles();`public static class StaticFileExtensions{   // Methodspublic static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app)    {        if (app == null){            throw new ArgumentNullException("app");}        return UseMiddlewareExtensions.UseMiddleware<StaticFileMiddleware>(app, Array.Empty<object>());}
}

可以看到 app.UseDeveloperExceptionPage()app.UseStaticFiles()等等都是通过中间件实现的。

怎么样自定义自己的中间件

背景:我们项目使用到中间件的情景是,需要和其他部门进行用户(User)信息的共享。 以平台和子系统举例,我们正在开发一个子系统,其中用户信息,登录,注册等功能是放在平台上的,这是一个跨多语言的系统,平台是Java语言开发,用户在访问子系统的一些页面的时候需要验证是否登录,另外一些页面是不需要验证是否登录的,所以需要一个身份验证系统来代替Identity的功能。

幸运的是微软已经给我们提供了一套身份验证的中间件,在Microsoft.AspNetCore.Authentication命名空间下,我们只需要拓展,添加自己的功能就行了 。具体怎么做呢?直接看代码吧。

根据约定俗成,中间件类需要有一个Invoke方法,签名是public async Task Invoke(HttpContext context){},下面是一个中间件的示例类:

public class RequestLoggerMiddleware{    private readonly RequestDelegate _next;    private readonly ILogger _logger;    public RequestLoggerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)    {_next = next;_logger = loggerFactory.CreateLogger<RequestLoggerMiddleware>();}    public async Task Invoke(HttpContext context)    {_logger.LogInformation("Handling request: " + context.Request.Path);        await _next.Invoke(context);_logger.LogInformation("Finished handling request.");}
}

了解了上面的约定之后,我们就开始定义我们自己的中间件Class。

我们需要一个流程图来理清逻辑思路,以便于写代码的时候思路更加的清晰。

平台有一个要求就是,用户在我们子系统退出之后,要调用平台的一个接口通知他们,他们要做一些后续的业务。

OK,开始撸码。

  • 首先创建一个PlatformAuthoricationMiddleware,它继承于Microsoft.AspNetCore.Authentication下的类AuthenticationMiddleware,由于AuthenticationMiddleware已经实现了Invoke功能,所以我们只需要重写(override)它里面的一些方法就可以了。等等,我们好像还需要一些配置,比如流程图中的ReturnUrl,平台的Cookie的Key值,平台验证用户合法性的接口地址等参数。

  • 建立一个Options类进行配置的设置,我们取名字为:PlatformAuthenticationOptions,继承AuthenticationOptions,并且实现掉IOptions<T>接口,这样子就能在Startup中直接配置了。

  • 我们只需要重写AuthenticationMiddleware中的CreateHandler方法就行了,在Handler中可以实现掉我们中间件的功能。

  • 然后创建一个处理的Handler类,取名为PlatformAuthenticationHandler,继承于AuthenticationHandler<TOptions>用来处理请求中的调用。

至此,我们的核心需要的类已经建立完了,剩下的就是填充代码。

  1. PlatformAuthenticationHandler中重写HandleAuthenticateAsync()方法 , 进行主流程的控制。

  2. PlatformAuthenticationHandler中重写FinishResponseAsync()方法,进行Session的存储操作。

  3. PlatformAuthenticationHandler中重写HandleSignOutAsync()方法,进行登出的控制,因为用户登出之后我们要通知平台做一些其他操作。

  4. PlatformAuthenticationHandler中重写HandleUnauthorizedAsync()方法,进行未认证操作。

最后,我们需要一个扩展类来把我们的中间件以扩展方法注册到管道当中去 。

public static class MiddlewareExtensions{    public static IApplicationBuilder UsePlatformAuthentication(this IApplicationBuilder app) {        if (app == null) {            throw new ArgumentNullException(nameof(app));}        return app.UseMiddleware<PlatformAuthenticationMiddleware>();}    public static IApplicationBuilder UsePlatformAuthentication(this IApplicationBuilder app, CookieAuthenticationOptions options) {        if (app == null) {            throw new ArgumentNullException(nameof(app));}        if (options == null) {            throw new ArgumentNullException(nameof(options));}        return app.UseMiddleware<PlatformAuthenticationMiddleware>(Options.Create(options));}
}

Startup中就是app.UsePlatformAuthentication()

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {loggerFactory.AddConsole(Configuration.GetSection("Logging"));    //注册PlatformAuthentication中间件app.UsePlatformAuthentication(new PlatformAuthenticationOptions() {UserSessionStore = new UserSessionStore(),});app.UseMvc();
}

现在,我们的中间件核心业务流程的实现已经出来了,我就不大篇幅的粘贴代码了,会影响阅读,感兴趣具体实现的朋友可以去下面的地址查看代码,有具体流程的注释。

示例源码:
https://github.com/yuleyule66/PlatformAuthMiddleware

相关文章:


原文地址:http://www.cnblogs.com/savorboard/p/5586229.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/329172.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一分钟实现分布式锁

转载自 一分钟实现分布式锁一、缘起 分布式环境下&#xff0c;多台机器上多个进程对一个数据进行操作&#xff0c;如果不做互斥&#xff0c;就有可能出现“余额扣成负数”&#xff0c;或者“商品超卖”的情况&#xff0c;如何实现简易分布式锁&#xff0c;对分布式环境下的临界…

java建立线性表的链式结构_Java实现线性表的链式存储

本文实例为大家分享了Java实现线性表的链式存储&#xff0c;供大家参考&#xff0c;具体内容如下链表&#xff1a;一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。package algorithm.datastructure.linklist;import java.ut…

dotnet core 开发体验之Routing

开始 回顾上一篇文章&#xff1a;dotnet core开发体验之开始MVC 里面体验了一把mvc,然后我们知道了aspnet mvc是靠Routing来驱动起来的&#xff0c;所以感觉需要研究一下Routing是什么鬼。 Routing简单使用体验 首先我们用命令yo aspnet创建一个新的空web项目。&#xff08;Yeo…

线程间协作的两种方式:wait、notify、notifyAll和Condition

转载自 线程间协作的两种方式&#xff1a;wait、notify、notifyAll和Condition在前面我们将了很多关于同步的问题&#xff0c;然而在现实中&#xff0c;需要线程之间的协作。比如说最经典的生产者-消费者模型&#xff1a;当队列满时&#xff0c;生产者需要等待队列有空间才能继…

格密码基础:q-ary格

目录 一. 格密码的重要性 二. 格密码基础 2.1 格点的另一种理解方式 三. q-ary格 3.1 q-ary垂直格 3.2 q-ary格 3.3 二者结合 四. 论文中的q-ary格 4.1 定理1 4.2 定理2 4.3 定理3 一. 格密码的重要性 格密码的基础是研究格点上的困难问题&#xff0c;这种格点使用…

java动脑公开课_java课堂动手动脑

实验任务一&#xff1a;阅读并运行示例PassArray.java.1)源代码&#xff1a;package demo;//PassArray.java//Passing arrays and individual array elements to methodspublic class PassArray {public static void main(String[] args) {int a[] { 1, 2, 3, 4, 5 };String o…

dotnet core开发体验之开始MVC

开始 在上一篇文章&#xff1a;dotnet core多平台开发体验 &#xff0c;体验了一把dotnet core 之后&#xff0c;现在想对之前做的例子进行改造&#xff0c;想看看加上mvc框架是一种什么样的体验&#xff0c;于是我就要开始诞生今天的这篇文章来分享我的感受了。 一、项目改造加…

Java时间处理第三方包:Joda-Time

转载自 Java时间处理第三方包&#xff1a;Joda-TimeJoda-Time provides a quality replacement for the Java date and time classes.Joda-Time is the de facto standard date and time library for Java prior to Java SE 8. Users are now asked to migrate to java.time (…

Visual Studio Code五月版本更新

开源项目、跨平台代码编辑器Visual Studio Code刚发布了其1.2版本&#xff08;虽然说是2016年5月发布&#xff0c;但其实是在6月交付的&#xff09;。和往常一样&#xff0c;这次发布的版本中对于很大一部分功能都进行了改善&#xff0c;其中最值得关注的可能是一种全新整合的终…

关于SimpleDateFormat时间格式化线程安全问题

转载自 关于SimpleDateFormat时间格式化线程安全问题昨天推送的文章《关于创建和销毁对象》一文中&#xff0c;2.1重复利用对象这一小节所举的SimpleDateFormat格式化时间的例子是不合适的&#xff0c;因为多线程场景下&#xff0c;SimpleDateFormat存在线程安全问题。在此&am…

聊聊ASP.NET Core默认提供的这个跨平台的服务器——KestrelServer

跨平台是ASP.NET Core一个显著的特性&#xff0c;而KestrelServer是目前微软推出了唯一一个能够真正跨平台的Server。KestrelServer利用一个名为KestrelEngine的网络引擎实现对请求的监听、接收和响应。KetrelServer之所以具有跨平台的特质&#xff0c;源于KestrelEngine是在一…

pdfbox java.lang.outofmemoryerror_Apache PDFBox 1.8.11 发布,Java 的 PDF 处理类

Apache PDFBox 1.8.11 发布&#xff0c;此版本是个增量 bug 修复版本&#xff0c;包括大量 bug 修复和改进。现已提供下载&#xff1a;主要改进内容&#xff1a;Bug 修复[PDFBOX-962] - All sort of Problems when importing Xfdf files into PDFs ->damaged pdfs and NPEs[…

Java8 Striped64 和 LongAdder

转载自 Java8 Striped64 和 LongAdder 数据 STRIPING 根据维基百科的这段说明&#xff1a;In computer data storage, data striping is the technique of segmenting logically sequential data, such as a file, so that consecutive segments are stored on different phys…

Roslyn项目系统简介

发布15年后&#xff0c;Microsoft终于开始替换Visual Studio中基于COM的C#和Visual Basic项目系统。Microsoft谈及很多有必要放弃目前所用系统的原因&#xff1a; 原生且基于COM单线程并与UI线程绑定难以通过和&#xff08;不同用途的&#xff09;子类型类扩展到聚合之外与Visu…

fastdfs 集群 java_FastDFS集群部署(转载 写的比较好)

之前介绍过关于FastDFS单机部署&#xff0c;详见博文&#xff1a;FastDFSNginx(单点部署)事例下面来玩下FastDFS集群部署&#xff0c;实现高可用(HA)服务器规划&#xff1a;跟踪服务器1【主机】(Tracker Server)&#xff1a;192.100.139.121跟踪服务器2【备机】(Tracker Server…

简析.NET Core 以及与 .NET Framework的关系

至2002微软公司推出.NET平台已近15年&#xff0c;在互联网快速迭代的浪潮中&#xff0c;许多语言已被淘汰&#xff0c;同时也有更多新的语言涌现&#xff0c;但 .Net 依然坚挺的站在系统开发平台的一线阵营中&#xff0c;并且随着.NET Core 即将到来(2016年6月27日)的正式版&am…

Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator

转载自 Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator功能简介&#xff1a;LongAdder是jdk1.8提供的累加器&#xff0c;基于Striped64实现。它常用于状态采集、统计等场景。AtomicLong也可以用于这种场景&#xff0c;但在线程竞争激烈的情况下&#xff0c;Long…

mysql 密码hash算法_如何用hash创建一个mySQL用户(‘sha256’,$salt.$password)?

我肯定错过了什么.我想为select-only事务设置数据库用户帐户,但mysql不允许我在创建用户帐户时选择密码的哈希方法.这失败了&#xff1a;GRANT SELECT ON myDB.* TO selectuserlocalhostIDENTIFIED BY hash(sha256, salted-myfakelongrandompasswordstring);错误1064(42000)&am…

为什么微软逐步转变为开源公司

微软目前拥有自己的 BSD Unix 操作系统&#xff0c;支持 Ubuntu 作为 Windows 10 的一个子系统&#xff0c;最近又将 Xamarin 软件开发工具包开源&#xff0c;所有这些意味着微软已不再是比尔盖茨和史蒂夫鲍尔默的微软了。 我知道这很难令人相信&#xff0c;但微软确实正大步走…

Jdk1.8 JUC源码增量解析(1)-atomic-Striped64

转载自 Jdk1.8 JUC源码增量解析(1)-atomic-Striped64功能简介&#xff1a;Striped64是jdk1.8提供的用于支持如Long累加器&#xff0c;Double累加器这样机制的基础类。Striped64的设计核心思路就是通过内部的分散计算来避免竞争(比如多线程CAS操作时的竞争)。Striped64内部包含…