前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >ASP.NET Core Middleware

ASP.NET Core Middleware

作者头像
雪飞鸿
发布于 2018-10-10 02:49:02
发布于 2018-10-10 02:49:02
73500
代码可运行
举报
文章被收录于专栏:me的随笔me的随笔
运行总次数:0
代码可运行

中间件(Middleware)是ASP.NET Core中的一个重要特性。所谓中间件就是嵌入到应用管道中用于处理请求和响应的一段代码。ASP.NET Core Middleware可以分为两种类型:

Conventional Middleware

这种中间件没有实现特定的接口或者继承特定类,它更像是Duck Typing (你走起路来像个鸭子, 叫起来像个鸭子, 那么你就是个鸭子)。有两种表现形式:

匿名方法

这种方式又称为内联中间件(in-line middleware),可以使用RunMapUse,MapWhen等扩展方法来实现。如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            // Do work that doesn't write to the Response.
            await next.Invoke();
            // Do logging or other work that doesn't write to the Response.
        });
    }
}

IApplicationBuilder的扩展方法:RunMapMapWhenUse(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware),最终都会调用IApplicationBuilder接口中的Use(Func<RequestDelegate, RequestDelegate> middleware)方法来实现向请求处理管道中注入中间件,后面会对源码做分析。

自定义中间件类

这种形式利于代码的复用,如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class XfhMiddleware
{
    private readonly RequestDelegate _next;

    //在应用程序的生命周期中,中间件的构造函数只会被调用一次
    public XfhMiddleware(RequestDelegate next)
    {
        this._next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Do something...
        await _next(context);
    }
}

public static class XfhMiddlewareExtension
{
    public static IApplicationBuilder UseXfhMiddleware(this IApplicationBuilder builder)
    {
        // 使用UseMiddleware将自定义中间件添加到请求处理管道中
        return builder.UseMiddleware<XfhMiddleware>();
    }
}

将自定义中间件配置到请求处理管道中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseXfhMiddleware();
}

IMiddleware

IMiddleware提供了强类型约束的中间件,其默认实现是MiddlewareFactory,接口定义如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface IMiddleware
{
    Task InvokeAsync(HttpContext context, RequestDelegate next);
}

IMiddlewareFactory用于创建IMiddleware实例及对实例进行回收,接口定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface IMiddlewareFactory
{
    public IMiddleware Create (Type middlewareType);
    
    public void Release (IMiddleware middleware);
}

自定义IMiddleware类型中间件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MyMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        await next(context);
    }
}


public static class MyMiddlewareExtensions
{
    public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleware>();
    }
}

将中间件注入到请求处理管道:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseMyMiddleware();
}

使用IMiddleware类型的中间件需要在容器中进行注册,否则抛异常,具体原因下面分析:

将中间件注入到容器中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<MyMiddleware>();
    services.AddMvc();
}

一段警告

下面贴一段微软文档中的警告,大意是不要试图去改变已发往客户端的响应内容,否则可能会引发异常。实在是太懒了,不想翻译就把原文贴出来了:

Warning Don't call next.Invoke after the response has been sent to the client. Changes to HttpResponse after the response has started throw an exception. For example, changes such as setting headers and a status code throw an exception. Writing to the response body after calling next:

  • May cause a protocol violation. For example, writing more than the stated Content-Length.
  • May corrupt the body format. For example, writing an HTML footer to a CSS file.

HasStarted is a useful hint to indicate if headers have been sent or the body has been written to.


UseMiddleware

前面将自定义中间件注入到请求处理管道时用到了UseMiddleware方法,从方法签名中可以看到UserMiddleware可以接受多个参数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static class UseMiddlewareExtensions
{
    public static IApplicationBuilder UseMiddleware<TMiddleware>(this IApplicationBuilder app,  
            params object[] args);
   
    public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, 
            Type middleware, params object[] args);
}

接下来我们看下UserMiddleware方法的具体实现,由于该方法代码量较大,所以这里只看其中的关键部分,方法整体流程如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware,
    params object[] args)
{
    // IMiddleware类型
    if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo()))
    {
        // IMiddleware doesn't support passing args directly since it's
        // activated from the container
        if (args.Length > 0)
        {
            throw new NotSupportedException(
                Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
        }
        return UseMiddlewareInterface(app, middleware);
    }
    
    // Conventional Middleware
    var applicationServices = app.ApplicationServices;
    return app.Use(next =>
    {
        // 判断传入的中间件是否符合约束
    });
}
  • 该方法首先判断传入的middleware是否是IMiddleware类型,如果是则调用UseMiddlewareInterface

从这段代码中可以看到IMiddlewareFactory负责创建并回收IMiddleware对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static class UseMiddlewareExtensions
{
    private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, Type middlewareType)
    {
        return app.Use(next =>
        {
            return async context =>
            {
                // 从容器中获取IMiddlewareFactory实例
                var middlewareFactory =
                    (IMiddlewareFactory) context.RequestServices.GetService(typeof(IMiddlewareFactory));
                if (middlewareFactory == null)
                {
                    // No middleware factory
                    throw new InvalidOperationException(
                 Resources.FormatException_UseMiddlewareNoMiddlewareFactory(typeof(IMiddlewareFactory)));
                }
                
                var middleware = middlewareFactory.Create(middlewareType);
                if (middleware == null)
                {
                    // The factory returned null, it's a broken implementation
                    throw new InvalidOperationException(
                        Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(middlewareFactory.GetType(),
                            middlewareType));
                }
                
                try
                {
                    await middleware.InvokeAsync(context, next);
                }
                finally
                {
                    middlewareFactory.Release(middleware);
                }
            };
        });
    }
}

MiddlewareFactoryCreate方法中可以看到,IMiddleware实例是从容器中获取的,若容器中找不到则会抛出异常:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MiddlewareFactory : IMiddlewareFactory
{
    private readonly IServiceProvider _serviceProvider;
    
    public MiddlewareFactory(IServiceProvider serviceProvider)
    {
        this._serviceProvider = serviceProvider;
    }
    
    public IMiddleware Create(Type middlewareType)
    {
        return ServiceProviderServiceExtensions.GetRequiredService(this._serviceProvider, middlewareType) as IMiddleware;
    }
    
    public void Release(IMiddleware middleware)
    {
    }
}
  • 若是Conventional Middleware则判断传入的middleware是否符合约束

首先判断传入的middleware中是否仅包含一个名称为Invoke或InvokeAsync的公共实例方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// UseMiddlewareExtensions类中的两个常量
internal const string InvokeMethodName = "Invoke";
internal const string InvokeAsyncMethodName = "InvokeAsync";

// UserMiddleware方法
var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);
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));
}

其次判断方法的返回类型是否是Task

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var methodInfo = invokeMethods[0];
if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType))
{
    throw new InvalidOperationException(
        Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName,
            InvokeAsyncMethodName, nameof(Task)));
}

然后再判断,方法的第一个参数是否是HttpContext类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var parameters = methodInfo.GetParameters();
if (parameters.Length == 0 || parameters[0].ParameterType != typeof(HttpContext))
{
    throw new InvalidOperationException(
        Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, InvokeAsyncMethodName,
            nameof(HttpContext)));
}

对于InvokeInvokeAsync仅包含一个HttpContext类型参数的情况用到了反射(ActivatorUtilities.CreateInstance方法中)来构建RequestDelegate

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var ctorArgs = new object[args.Length + 1];
ctorArgs[0] = next;
Array.Copy(args, 0, ctorArgs, 1, args.Length);
var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middlewa
if (parameters.Length == 1)
{
    return (RequestDelegate) methodInfo.CreateDelegate(typeof(RequestDelegate), in
}

对于包含多个参数的情况,则使用了表达式树来构建RequestDelegate

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var factory = Compile<object>(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);
};

完整的代码可以在Github上看到。

Use(Func<RequestDelegate, RequestDelegate> middleware)

上述所有中间件,最终都会调用IApplicationBuilder接口中的Use(Func<RequestDelegate, RequestDelegate> middleware)方法来实现向请求处理管道中注册中间件,该方法在ApplicationBuilder类的实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ApplicationBuilder : IApplicationBuilder
{
    private readonly IList<Func<RequestDelegate, RequestDelegate>> _components =
        new List<Func<RequestDelegate, RequestDelegate>>();

    public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
    {
        this._components.Add(middleware);
        return this;
    }
}

从上面代码中可以看到,中间件是一个RequestDelegate类型的委托,请求处理管道其实是一个委托列表,请求委托签名如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public delegate Task RequestDelegate(HttpContext context);

与ASP.NET处理管道的区别

传统的ASP.NET的处理管道是基于事件模型的,处理管道有多个IHttpModule和一个IHttpHandler组成。请求处理管道中各个模块被调用的顺序取决于两方面:

  • 模块所注册事件被触发的先后顺序
  • 注册同一事件的不同模块执行先后顺序有Web.config中的配置顺序决定

ASP.NET Core的请求处理管道则是有一堆中间件组成,相对ASP.NET更简单。

中间件处理请求和响应的顺序只与其在代码中的注册顺序有关:处理请求按注册顺序依次执行,处理响应按注册顺序反方向依次执行。

其次,在ASP.NET Core中只需使用代码,而无需使用Global.asaxWeb.config来配置请求处理管道。

小结

所谓中间件就是嵌入到应用管道中用于处理请求和响应的一段代码,它主要有两个作用:

  • 处理请求和响应
  • 可以阻止请求发往请求处理管道中的下一个中间件

在ASP.NET Core中,中间件是以RequestDelegate委托的形式体现的。

ASP.NET Core中整个请求处理管道的创建是围绕这种IApplicationBuilder接口进行的,请求处理管道是一个List<RequestDelegate>类型的列表。

推荐阅读

ASP.NET Core Middleware Factory-based middleware activation in ASP.NET Core Migrate HTTP handlers and modules to ASP.NET Core middleware ASP.NET MVC5请求处理管道和生命周期 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-09-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
时序分析笔记系列(四)、系统时序题目分析
假设存在posetive clock skew为10ns,问最高电路电路频率?系统能忍受的最大posetive clock skew。(Tset_up=1ns 、Thold=1ns 、Tcllk_q=1ns )?
根究FPGA
2020/06/29
1.4K0
【干货】八小时超长视频教你掌握FPGA时序约束!
注:零基础学会FPGA时序约束。本期推送配套相关独家教学视频,关注公众号,后台回复“时序分析培训”或留下您的邮箱,即可获取视频链接。视频内容是由团队郑圆圆同学讲解,欢迎批评指正。以下是节选的视频片段。
网络交换FPGA
2020/06/02
4.1K3
【干货】八小时超长视频教你掌握FPGA时序约束!
《数字集成电路静态时序分析基础》笔记⑪
opening edge指边沿跳变以后有效的边沿,closing edge指边沿跳变之后无效的边沿
空白的贝塔
2020/06/24
1.2K0
《数字集成电路静态时序分析基础》笔记⑪
FPGA 高级设计:时序分析和收敛
什么是静态时序分析?静态时序分析就是Static Timing Analysis,简称 STA。它可以简单的定义为:设计者提出一些特定的时序要求(或者说是添加特定的时序约束),套用特定的时序模型,针对特定的电路进行分析。分析的最终结果当然是要求系统时序满足设计者提出的要求。
FPGA技术江湖
2020/12/30
1.2K0
【002】数字IC笔面试常见题
建立时间是指触发器的时钟信号上升沿到来之前,数据保持稳定不变的时间;保持时间是指触发器的时钟信号上升沿到来之后,数据保持稳定不变的时间;
数字IC小站
2022/08/26
5100
FPGA中的时序约束--从原理到实例
建立时间和保持时间是FPGA时序约束中两个最基本的概念,同样在芯片电路时序分析中也存在。
网络交换FPGA
2019/10/29
6.2K0
FPGA中的时序约束--从原理到实例
时序分析笔记系列(二)、启动沿与锁存沿
Launch Edge:启动边沿,指的是产生数据的register1所使用的时钟的上升沿。
根究FPGA
2020/06/29
1.8K0
【vivado学习五】时序分析
典型的时序模型由发起寄存器、组合逻辑和捕获寄存器3部分组成,如图1所示形成了三条时钟路径:原时钟路径(Source Clock path)、数据时钟路径(Data path)、目的时钟路径(Destination Clock path)。
FPGA开源工作室
2019/10/29
1.6K0
【vivado学习五】时序分析
时序分析笔记系列(一)、建立与保持时间etc.
Tco即D触发器时钟到输出延时,指的是时钟信号在寄存器引脚上发生转变之后,在由寄存器的数据输出引脚上获得有效输出所需要的最大时间,也叫做Tclk_q。
根究FPGA
2020/06/29
2.8K0
静态时序分析的基本概念和方法
在同步电路设计中,时序是一个非常重要的因素,它决定了电路能否以预期的时钟速率运行。为了验证电路的时序性能,我们需要进行静态时序分析,即在最坏情况下检查所有可能的时序违规路径,而不需要测试向量和动态仿真。本文将介绍静态时序分析的基本概念和方法,包括时序约束,时序路径,时序裕量,setup检查和hold检查等。
AsicWonder
2023/09/01
4180
静态时序分析的基本概念和方法
FPGA中的亚稳态
在FPGA系统中,如果数据传输中不满足触发器的 Tsu和 Th不满足,或者复位过程中复位信号的释放相对于有效时钟沿的恢复时间(recovery time)不满足,就可能产生亚稳态,此时触发器输出端Q在有效时钟沿之后比较长的一段时间处于不确定的状态,在这段时间里Q端在0和1之间处于振荡状态,而不是等于数据输入端D的值。这段时间称为决断时间(resolution time)。经过resolution time之后Q端将稳定到0或1上,但是稳定到0或者1,是随机的,与输入没有必然的关系。
数字芯片社区
2020/07/20
1.4K1
FPGA中的亚稳态
建立时间和保持时间(setup time 和 hold time)
同步时序电路设计中,只在时钟的上升沿或下降沿进行采样。为了正确得到采样结果,需要确保采样时刻数据有效,因此工具会对综合结果进行静态时序分析,以判断时钟和数据之间的相对关系是否满足要求。以寄存器-寄存器之间的路径为例子
sea-wind
2019/07/31
5.3K0
建立时间和保持时间(setup time 和 hold time)
静态时序分析及setup&hold时序违例修复
STA用于分析设计中的所有时序路径是否都时序收敛,其不需要输入激励。对于数字芯片设计工程师,必须要了解不同的时序路径和相关的STA概念。
AsicWonder
2020/06/11
3.8K0
时序分析笔记系列(三)、系统最大时钟频率计算
Tclk >= Tco + Tlogic + Trouting + Tsu - Tskew
根究FPGA
2020/06/29
5.4K0
时序分析中的基本概念和术语
1.建立保持时间 2.四种时序路径 第一类时序路径:从设备A的时钟到FPGA的第一级寄存器的数据输入端口 第二类时序路径:两个同步原件之间的路径,比如rega时钟端口到regb的数据端口 第三类
瓜大三哥
2018/02/24
1.5K0
时序分析中的基本概念和术语
FPGA STA(静态时序分析)
大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。
FPGA技术江湖
2020/12/29
1.5K1
FPGA STA(静态时序分析)
《数字集成电路静态时序分析基础》笔记⑦
下面是一个timing report,起点是UFF0,终点是UFF1,path group是按照终点时钟分类的,所以是CLKM。path type max代表检查最大的路径延迟,point表示途径点,incr代表经过这个点的增量,path代表累计延迟。r和f代表rise和fall。
空白的贝塔
2020/06/24
1.5K0
《数字集成电路静态时序分析基础》笔记⑦
FPGA基础知识极简教程(7)详解亚稳态与跨时钟域传输
这篇文章主要是对过去对于亚稳态以及跨时钟域传输问题的一次总结,作为这个系列博文的一次梳理吧。注:微信公众号也会更新,欢迎大家关注,我有了新文章会通过微信公众号推送通知大家,让你有选择的看到我的最新动态。
Reborn Lee
2020/06/29
1.4K0
从fan-in、fan-out看setup和hold time violation
保持时间的目的是防止下一次的数据传输过快,将本次的数据冲刷掉,是对上次数据时间的约束。经过Tsu建立时间之后,触发器进入建立时间阶段,在该阶段最担心的问题是下一次的数据来的太快,导致还未满足保持时间的要求。
根究FPGA
2020/06/30
1.4K0
FPGA必出笔试题
1、用状态机实现10010码的探测,如x=1001001000 z=0000100100(输出)
碎碎思
2024/03/22
3560
FPGA必出笔试题
相关推荐
时序分析笔记系列(四)、系统时序题目分析
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • Conventional Middleware
    • 匿名方法
    • 自定义中间件类
  • IMiddleware
  • 一段警告
  • UseMiddleware
  • Use(Func<RequestDelegate, RequestDelegate> middleware)
  • 与ASP.NET处理管道的区别
  • 小结
  • 推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档