ASP.NET Core中间件初始化的实现

前言

在日常使用ASP.NET Core开发的过程中我们多多少少会设计到使用中间件的场景,ASP.NET Core默认也为我们内置了许多的中间件,甚至有时候我们需要自定义中间件来帮我们处理一些请求管道过程中的处理。接下来,我们将围绕着以下几个问题来简单探究一下,关于ASP.NET Core中间件是如何初始化的

  • 首先,使用UseMiddleware注册自定义中间件和直接Use的方式有何不同
  • 其次,使用基于约定的方式定义中间件和使用实现IMiddleware接口的方式定义中间件有何不同
  • 再次,使用基于约定的方式自定义中间件的究竟是如何约束我们编写的类和方法格式的
  • 最后,使用约定的方式定义中间件,通过构造注入和通过Invoke方法注入的方式有何不同

接下来我们将围绕这几个核心点来逐步探究关于ASP.NET Core关于中间件初始化的神秘面纱,来指导我们以后使用它的时候需要有注意点,来减少踩坑的次数。

自定义的方式

使用自定义中间件的方式有好几种,咱们简单来演示一下三种比较常用方式。

Use方式

首先,也是最直接最简单的使用Use的方式,比如

app.Use(async (context, next) =>
{
    var endpoint = context.Features.Get<IEndpointFeature>()?.Endpoint;
    if (endpoint != null)
    {
        ResponseCacheAttribute responseCache = endpoint.Metadata.GetMetadata<ResponseCacheAttribute>();
        if (responseCache != null)
        {
            //做一些事情
        }
    }
    await next();
});

基于约定的方式

然后使用UseMiddleware也是我们比较常用的一种方式,这种方式使用起来相对于第一种来说,虽然使用起来可能会稍微繁琐一点,毕竟需要定义一个类,但是更好的符合符合面向对象的封装思想,它的使用方式大致如下,首先定义一个Middleware的类

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;
    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery))
        {
            var culture = new CultureInfo(cultureQuery);
            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }
        await _next(context);
    }
}

编写完成之后,需要手动的将类注册到管道中才能生效,注册方式如下所示

app.UseMiddleware<RequestCultureMiddleware>();

实现IMiddleware的方式

还有一种方式是实现IMiddleware接口的方式,这种方式比如前两种方式常用,但是也确确实实的存在于ASP.NET Core中,既然存在也就有它存在的理由,我们也可以探究一下,它的使用方式也是需要自定义一个类去实现IMiddleware接口,如下所示

public class RequestCultureOtherMiddleware:IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery))
        {
            var culture = new CultureInfo(cultureQuery);
            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }
        await next(context);
    }
}

这种方式和第二种方式略有不同,需要手动将中间件注册到容器中,至于声明周期也没做特殊要求,可以直接注册为单例模式

services.AddSingleton<IMiddleware,RequestCultureOtherMiddleware>();

完成上步操作之后,同样也需要将其注册到管道中去

app.UseMiddleware<RequestCultureOtherMiddleware>();

这种方式相对于第二种方式的主要区别在于灵活性方面的差异,它实现了IMiddleware接口,那就要受到IMiddleware接口的约束,也就是我们常说的里氏代换原则,首先我们可以先来看下IMiddleware接口的定义[点击查看源码👈]

public interface IMiddleware
{
 /// <summary>
 /// 请求处理方法
 /// </summary>
 /// <param name="context">当前请求上下文</param>
 /// <param name="next">请求管道中下一个中间件的委托</param>
 Task InvokeAsync (HttpContext context, RequestDelegate next);
}

通过这个接口也就看出来InvokeAsync只能接受HttpContext和RequestDelegate参数,无法定义其他形式的参数,也没办法通过注入的方式编写InvokeAsync方法参数,说白了就是没有第二种方式灵活,受限较大。
关于常用的自定义中间件的方式,我们就先说到这里,我们也知道了如何定义使用中间件。接下来我们就来探讨一下,这么多种方式之间到底存在怎样的联系。

源码探究

上面我们已经演示了关于使用中间件的几种方式,那么这么几种使用方式之间有啥联系或区别,我们只看到了表面的,接下来我们来看一下关于中间件初始化的源码来一探究竟。
首先,无论那种形式都是基于IApplicationBuilder这个接口扩展而来的,所以我们先从这里下手,找到源码IApplicationBuilder位置[点击查看源码👈]可以看到以下代码

/// <summary>
/// 将中间件委托添加到应用程序的请求管道。
/// </summary>
/// <param name="middleware">中间件委托</param>
/// <returns>The <see cref="IApplicationBuilder"/>.</returns>
IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);

IApplicationBuilder接口里只有Use的方式可以添加中间件,由此我们可以大致猜到两点信息

  • 其它添加中间件的方式,都是在扩展自IApplicationBuilder,并不是IApplicationBuilder本身的方法。
  • 其它添加中间件的形式,最终都会转换为Use的方式。

Use扩展方法

上面我们看到了IApplicationBuilder只包含了一个Use方法,但是我们日常编程中最常使用到的却并不是这一个,而是来自UseExtensions扩展类的Use扩展方法,实现如下所示[点击查看源码👈]

public static IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware)
{
   //将middleware转换为Use(Func<RequestDelegate, RequestDelegate> middleware)的形式
    return app.Use(next =>
    {
        return context =>
        {
            Func<Task> simpleNext = () => next(context);
            return middleware(context, simpleNext);
        };
    });
}

如预料的那样,Use的扩展方法最终都会转换为Use(Func<RequestDelegate, RequestDelegate> middleware)的形式去执行。Use扩展方法的形式还是比较清晰的,毕竟也是基于委托的形式,而且参数是固定的。

UseMiddleware

上面我们看到了Use的扩展方法,它最终还是转换为Use(Func<RequestDelegate, RequestDelegate> middleware)的形式去执行。接下来我们来看下通过编写类的形式定义中间件会是怎样的转换操作。找到UseMiddleware扩展方法所在的地方,也就是UseMiddlewareExtensions扩展类里[点击查看源码👈],我们最常用的是UseMiddleware这个方法,而且这个方法是UseMiddlewareExtensions扩展类的入口方法[点击查看源码👈],说白了就是它是完全调用别的方法没有自己的实现逻辑

/// <summary>
/// 将中间件类型添加到应用程序的请求管道.
/// </summary>
/// <typeparam name="TMiddleware">中间件类型</typeparam>
/// <param name="args">传递给中间件类型实例的构造函数的参数.</param>
/// <returns>The <see cref="IApplicationBuilder"/> instance.</returns>
public static IApplicationBuilder UseMiddleware<[DynamicallyAccessedMembers(MiddlewareAccessibility)]TMiddleware>(this IApplicationBuilder app, params object[] args)
{
    return app.UseMiddleware(typeof(TMiddleware), args);
}

继续向下看找到它调用的扩展方法,在展示该方法之前我们先罗列一下该类的常量属性,因为类中的方法有用到,如下所示

internal const string InvokeMethodName = "Invoke";
internal const string InvokeAsyncMethodName = "InvokeAsync";

从这里我们可以得到一个信息,基于约定的形式自定义的中间件触发方法名可以是Invoke或InvokeAsync

继续看执行方法的实现代码

public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, [DynamicallyAccessedMembers(MiddlewareAccessibility)] Type middleware, params object[] args)
{
    //判断自定义的中间件是否是实现了IMiddleware接口
    if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo()))
    {
        //Middleware不支持直接传递参数
        //因为它是注册到容器中的,所以不能通过构造函数传递自定义的参数,否则抛出异常
        if (args.Length > 0)
        {
            throw new NotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
        }
        //实现IMiddleware接口的中间件走的是这个逻辑,咱们待会看
        return UseMiddlewareInterface(app, middleware);
    }

    var applicationServices = app.ApplicationServices;
    return app.Use(next =>
    {
        //获取自定义中间件类的非静态public方法
        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();
        //方法名为Invoke或InvokeAsync的方法只能有有一个,存在多个话会抛出异常
        if (invokeMethods.Length > 1)
        {
            throw new InvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName, InvokeAsyncMethodName));
        }
        //自定义的中间件类中必须包含名为Invoke或InvokeAsync的方法,否则也会抛出异常
        if (invokeMethods.Length == 0)
        {
            throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName, InvokeAsyncMethodName, middleware));
        }
        //名为Invoke或InvokeAsync的方法的返回值类型必须是Task类型,否则会抛出异常
        var methodInfo = invokeMethods[0];
        if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType))
        {
            throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, InvokeAsyncMethodName, nameof(Task)));
        }
        //获取Invoke或InvokeAsync方法的参数
        var parameters = methodInfo.GetParameters();
        //如果该方法不存在参数或方法的第一个参数不是HttpContext类型的实例,会抛出异常
        if (parameters.Length == 0 || parameters[0].ParameterType != typeof(HttpContext))
        {
            throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, InvokeAsyncMethodName, nameof(HttpContext)));
        }
        //定义新的数组比传递的参数长度多一个,为啥呢?往下看。
        var ctorArgs = new object[args.Length + 1];
        //因为方法数组的首元素是RequestDelegate类型的next
        //也就是基于约定定义的中间件构造函数的第一个参数是RequestDelegate类型的实例
        ctorArgs[0] = next;
        Array.Copy(args, 0, ctorArgs, 1, args.Length);
        //创建基于约定的中间件实例
        //又看到ActivatorUtilities这个类了,关于这个类有兴趣的可以研究一下,可以根据容器创建类型实例,非常好用
        var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);
        //如果Invoke或InvokeAsync方法只有一个参数,则直接创建RequestDelegate委托返回
        if (parameters.Length == 1)
        {
            //RequestDelegate其实就是public delegate Task RequestDelegate(HttpContext context);
            return (RequestDelegate)methodInfo.CreateDelegate(typeof(RequestDelegate), instance);
        }
        //编译Invoke或InvokeAsync方法,关于Compile的实现等会咱们再看
        var factory = Compile<object>(methodInfo, parameters);
        //返回这个委托
        //看着这个委托的格式有点眼熟,其实就是RequestDelegate即public delegate Task RequestDelegate(HttpContext context);
        return context =>
        {
            var serviceProvider = context.RequestServices ?? applicationServices;
            //serviceProvider不能为空,否则没法玩了
            if (serviceProvider == null)
            {
                throw new InvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
            }
            //返回委托执行结果
            return factory(instance, context, serviceProvider);
        };
    });
}

这个方法其实是工作的核心方法,通过这里可以看出来,自定义中间件的大致执行过程。代码中的注释我写的比较详细,有兴趣的可以仔细了解一下,如果懒得看我们就大致总结一下大致的核心点

  • 首先UseMiddleware的本质确实还是执行的Use方法
  • 实现IMiddleware接口的中间件走的是独立的处理逻辑,而且构造函数传递自定义的参数,因为它的数据来自于容器的注入。
  • 基于约定定义中间件的情况,即不实现IMiddleware的情况下。
    • ①基于约定定义的中间件,构造函数的第一个参数需要是RequestDelegate类型
    • ②查找方法名可以为Invoke或InvokeAsync,且存在而且只能存在一个
    • ③Invoke或InvokeAsync方法返回值需为Task,且方法的第一个参数必须为HttpContext类型
    • ④Invoke或InvokeAsync方法如果只包含HttpContext类型参数,则该方法直接转换为RequestDelegate
    • ⑤我们之所以可以通过构造注入在中间件中获取服务是因为基于约定的方式是通过ActivatorUtilities类创建的实例

通过上面的源码我们了解到了实现IMiddleware接口的方式自定义中间件的方式是单独处理的即在UseMiddlewareInterface方法中[点击查看源码👈],接下来我们查看一下该方法的代码

private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type middlewareType)
{
    return app.Use(next =>
    {
        return async context =>
        {
            var middlewareFactory = (IMiddlewareFactory?)context.RequestServices.GetService(typeof(IMiddlewareFactory));
            if (middlewareFactory == null)
            {
                // 没有middlewarefactory直接抛出异常
                throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoMiddlewareFactory(typeof(IMiddlewareFactory)));
            }
            //创建middleware实例
            var middleware = middlewareFactory.Create(middlewareType);
            if (middleware == null)
            {
                throw new InvalidOperationException(Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(middlewareFactory.GetType(), middlewareType));
            }

            try
            {
                //执行middleware的InvokeAsync方法
                await middleware.InvokeAsync(context, next);
            }
            finally
            {
                //释放middleware
                middlewareFactory.Release(middleware);
            }
        };
    });
}

通过上面的代码我们可以看到,IMiddleware实例是通过IMiddlewareFactory实例创建而来,ASP.NET Core中IMiddlewareFactory默认注册的实现类是MiddlewareFactory,接下来我们看下这个类的实现[点击查看源码👈]

public class MiddlewareFactory : IMiddlewareFactory
{
    private readonly IServiceProvider _serviceProvider;

    public MiddlewareFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IMiddleware? Create(Type middlewareType)
    {
        //根据类型从容器中获取IMiddleware实例
        return _serviceProvider.GetRequiredService(middlewareType) as IMiddleware;
    }

    public void Release(IMiddleware middleware)
    {
        //因为容器控制了对象的生命周期,所以这里啥也没有
    }
}

好吧,其实就是在容器中获取的IMiddleware实例,通过这个我们就可以总结出来实现IMiddleware接口的形式创建中间件的操作

  • 需要实现IMiddleware接口,来约束中间件的行为,方法名只能为InvokeAsync
  • 需要手动注册IMiddleware和实现类到容器中,生命周期可自行约束,如果生命周期为Scope或瞬时,那么每次请求都会创建新的中间件实例
  • 没办法通过InvokeAsync方法注入服务,因为受到了IMiddleware接口的约束

上面我们看到了实现IMiddleware接口的方式中间件是如何被初始化的,接下来我们继续来看,基于约定的方式定义的中间件是如何被初始化的。通过上面我们展示的源码可知,实现逻辑在Compile方法中,该方法整体实现方式就是基于Expression,主要原因个人猜测有两点,一个是形式比较灵活能应对的场景较多,二是性能稍微比反射好一点。在此之前,我们先展示一下Compile方法依赖的操作,首先反射是获取UseMiddlewareExtensions类的GetService方法操作

private static readonly MethodInfo GetServiceInfo = typeof(UseMiddlewareExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static)!;

其中GetService方法的实现如下所示,其实就是在容器ServiceProvider中获取指定类型实例

private static object GetService(IServiceProvider sp, Type type, Type middleware)
{
    var service = sp.GetService(type);
    if (service == null)
    {
        throw new InvalidOperationException(Resources.FormatException_InvokeMiddlewareNoService(type, middleware));
    }
    return service;
}

好了上面已将Compile外部依赖已经展示出来了,接下来我们就可以继续探究Compile方法了[点击查看源码👈]

private static Func<T, HttpContext, IServiceProvider, Task> Compile<T>(MethodInfo methodInfo, ParameterInfo[] parameters)
{
    var middleware = typeof(T);
    //构建三个Parameter名为httpContext、serviceProvider、middleware
    var httpContextArg = Expression.Parameter(typeof(HttpContext), "httpContext");
    var providerArg = Expression.Parameter(typeof(IServiceProvider), "serviceProvider");
    var instanceArg = Expression.Parameter(middleware, "middleware");

    //穿件Expression数组,且数组第一个参数为httpContextArg
    var methodArguments = new Expression[parameters.Length];
    methodArguments[0] = httpContextArg;
    //因为Invoke或InvokeAsync方法第一个参数为HttpContext,且methodArguments第一个参数占位,所以跳过第一个参数
    for (int i = 1; i < parameters.Length; i++)
    {
        //获取方法参数
        var parameterType = parameters[i].ParameterType;
        //不支持ref类型操作
        if (parameterType.IsByRef)
        {
            throw new NotSupportedException(Resources.FormatException_InvokeDoesNotSupportRefOrOutParams(InvokeMethodName));
        }

       //构建参数类型表达式,即用户构建方法参数的操作
        var parameterTypeExpression = new Expression[]
        {
            providerArg,
            Expression.Constant(parameterType, typeof(Type)),
            Expression.Constant(methodInfo.DeclaringType, typeof(Type))
        };
        //声明调用GetServiceInfo的表达式
        var getServiceCall = Expression.Call(GetServiceInfo, parameterTypeExpression);
        //将getServiceCall操作转换为parameterType
        methodArguments[i] = Expression.Convert(getServiceCall, parameterType);
    }
    //获取中间件类型表达式
    Expression middlewareInstanceArg = instanceArg;
    if (methodInfo.DeclaringType != null && methodInfo.DeclaringType != typeof(T))
    {
        //转换中间件类型表达式类型与声明类型一致
        middlewareInstanceArg = Expression.Convert(middlewareInstanceArg, methodInfo.DeclaringType);
    }
    //调用middlewareInstanceArg(即当前中间件)的methodInfo(即获取Invoke或InvokeAsync)方法参数(methodArguments)
    var body = Expression.Call(middlewareInstanceArg, methodInfo, methodArguments);
    //转换为lambda
    var lambda = Expression.Lambda<Func<T, HttpContext, IServiceProvider, Task>>(body, instanceArg, httpContextArg, providerArg);
    return lambda.Compile();
}

上面的代码比较抽象,其实主要是因为它是基于表达式树进行各种操作的,如果对表达式树比较熟悉的话,可能对上面的代码理解起来还好一点,如果不熟悉表达式树的话,可能理解起来比较困难,不过还是建议简单学习一下Expression相关的操作,慢慢的发现还是挺有意思的,它的性能整体来说比传统的反射性能也会更好一点。其实Compile主要实现的操作转化为我们比较容易理解的代码的话就是下面所示的操作,如果我们编写了一个如下的中间件代码

public class Middleware
{
    public Task Invoke(HttpContext context, ILoggerFactory loggerFactory)
    {
    }
}

那么通过Compile方法将转换为类似以下形式的操作,这样说的话可能会好理解一点

Task Invoke(Middleware instance, HttpContext httpContext, IServiceProvider provider)
{
    return instance.Invoke(httpContext, (ILoggerFactory)UseMiddlewareExtensions.GetService(provider, typeof(ILoggerFactory));
}

通过上面的源码分析我们了解到,基于约定的方式定义的中间件实例是通过ActivatorUtilities类创建的,而且创建实例是在返回RequestDelegate委托之前,IApplicationBuilder的Use方法只会在首次运行的时候执行,后续管道串联执行的其实正是它返回的结果RequestDelegate这个委托。但是执行转换Invoke或InvokeAsync方法为执行委托的操作却是在返回的RequestDelegate委托当中,也就是我们每次请求管道会处理的逻辑中。这个逻辑可以在IApplicationBuilder默认的实现类ApplicationBuilder类的Build方法中可以得知[点击查看源码👈],它的实现逻辑如下所示

public RequestDelegate Build()
{
    //最后的管道处理,即请求未能匹配到任何终结点的情况
    RequestDelegate app = context =>
    {
        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);
        }
        //执行管道的重点是404,只有未命中任何终结点的情况下才会走到这里
        context.Response.StatusCode = StatusCodes.Status404NotFound;
        return Task.CompletedTask;
    };
    //_components即我们通过Use添加的中间件
    foreach (var component in _components.Reverse())
    {
       //得到执行结果即RequestDelegate
        app = component(app);
    }
    //返回第一个管道中间件
    return app;
}

通过上面的代码我们可以清楚的看到,管道最终执行的就是执行Func<RequestDelegate, RequestDelegate>这个委托的返回结果RequestDelegate。

由此得到结论,基于约定的中间件形式,通构造函数注入的服务实例,是和应用程序的生命周期一致的。通过Invoke或InvokeAsync方法注入的服务实例每次请求都会被执行到,即生命周期是Scope的。

总结

通过本次对源码的研究,我们认识到了自定义的ASP.NET Core中间件是如何被初始化的。虽然自定义的中间件的形式有许多种方式,但是最终还都是转换为IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)这种方式。将中间件抽离为独立的类有两种方式,即基于约定的方式和实现IMiddleware接口的形式,通过分析源码我们也更深刻的了解两种方式的不同之处。基于约定的方式更灵活,它的声明周期是单例的,但是通过它的Invoke或InvokeAsync方法注入的服务实例生命周期是Scope的。实现IMiddleware接口的方式生命周期取决于自己注册服务实例时候声明的周期,而且这种方式没办法通过方法注入服务,因为有IMiddleware接口InvokeAsync方法的约束。

当然不仅仅是我们在总结中说的的这些,还存在更多的细节,这些我们在分析源码的时候都有涉及,相信阅读文章比较仔细的同学肯定会注意到这些。阅读源码收获正是这些,解决心中的疑问,了解更多的细节,有助于在实际使用中避免一些不必要的麻烦。本次讲解就到这里,愿各位能有所收获。

到此这篇关于ASP.NET Core中间件初始化的实现的文章就介绍到这了,更多相关ASP.NET Core中间件初始化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入探究ASP.NET Core Startup初始化问题

    前言 Startup类相信大家都比较熟悉,在我们使用ASP.NET Core开发过程中经常用到的类,我们通常使用它进行IOC服务注册,配置中间件信息等.虽然它不是必须的,但是将这些操作统一在Startup中做处理,会在实际开发中带来许多方便.当我们谈起Startup类的时候你有没有好奇过以下几点 为何我们自定义的Startup可以正常工作. 我们定义的Startup类中ConfigureServices和Configure只能叫这个名字才能被调用到吗? 在使用泛型主机(IHostBuilder)

  • ASP.NET Core中间件初始化的实现

    前言 在日常使用ASP.NET Core开发的过程中我们多多少少会设计到使用中间件的场景,ASP.NET Core默认也为我们内置了许多的中间件,甚至有时候我们需要自定义中间件来帮我们处理一些请求管道过程中的处理.接下来,我们将围绕着以下几个问题来简单探究一下,关于ASP.NET Core中间件是如何初始化的 首先,使用UseMiddleware注册自定义中间件和直接Use的方式有何不同 其次,使用基于约定的方式定义中间件和使用实现IMiddleware接口的方式定义中间件有何不同 再次,使用基

  • 理解ASP.NET Core 中间件(Middleware)

    目录 中间件 中间件管道 Run Use UseWhen Map MapWhen Run & Use & UseWhen & Map & Map 编写中间件并激活 基于约定的中间件 基于工厂的中间件 基于约定的中间件 VS 基于工厂的中间件 中间件 先借用微软官方文档的一张图: 可以看到,中间件实际上是一种配置在HTTP请求管道中,用来处理请求和响应的组件.它可以: 决定是否将请求传递到管道中的下一个中间件 可以在管道中的下一个中间件处理之前和之后进行操作 此外,中间件的注

  • ASP.NET Core 中间件的使用之全局异常处理机制

    目录 1.创建项目 2.创建全局异常过滤器 3.依赖注入全局异常处理机制 4.测试全局异常处理机制 前言: 我们经常听到"秒修复秒上线",觉得很厉害的样子. 其实不然,这只是一个调侃而已,出现问题的方式很多(逻辑漏洞.代码异常.操作方式不正确等). 我们今天来说代码异常问题怎么快速定位,减少不必要的时间浪费. 这就是今天的主题"添加全局异常处理机制"捕捉异常存储到数据库(mongodb.SqlServer.MySQL等). PS:输出txt的话不怎么友好,不是所有人

  • 详解ASP.NET Core中间件Middleware

    本文为官方文档译文,官方文档现已非机器翻译 https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1 什么是中间件(Middleware)? 中间件是组装到应用程序管道中以处理请求和响应的软件. 每个组件: 选择是否将请求传递给管道中的下一个组件. 可以在调用管道中的下一个组件之前和之后执行工作. 请求委托(Request delegates)用于构建请求管道,处理每个HTT

  • ASP.NET Core中间件实现限流的代码

    目录 一.限流算法 1.计数器算法 1.1固定窗口算法 1.2滑动窗口算法 2.令牌桶算法 3.漏桶算法 二.ASP.NETCore中间件实现限流 1.中间件代码 2.在管道中的使用 一.限流算法 在高并发系统中,有三把利器用来保护系统:缓存.降级和限流. 本文主要是介绍限流,限流算法主要有以下三种: 1.计数器算法 固定窗口 滑动窗口 2.令牌桶算法 3.漏桶算法 1.计数器算法 1.1 固定窗口算法 计数器算法是限流算法里最简单也是最容易实现的一种算法.比如我们规定,对于A接口来说,我们1分

  • ASP.NET Core中间件用法与官方常用中间件介绍

    目录 一.什么是中间件 中间件和过滤器的区别 二.中间件常用方法 1.Run方法 2.Use方法 3.Map方法 4.Mapwhen方法 三.自定义中间件 四.官方常用中间件 1.异常处理中间件 2.HTTPS重定向中间件 3.静态文件中间件 4.Cookie中间件 5.路由中间件 6.身份认证中间件 7.授权中间件 8.会话中间件 9.终结点路由中间件 一.什么是中间件 我们都知道,任何的一个web框架都是把http请求封装成一个管道,每一次的请求都是经过管道的一系列操作,最终才会到达我们写的

  • ASP.NET Core中间件

    目录 1.前言 2.使用中间件 2.1 Run 2.2 Use 2.3 Map和MapWhen 3.顺序 4.编写中间件(重点) 4.1中间件类 4.2中间件扩展方法 5.按每次请求创建依赖注入(DI) 1.前言 整个HTTP Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline).而中间件(middleware)则是一种装配到请求管道以处理请求和响应的组件.每个组件: 可选择是否将请求传递到管道中的下一个组件. 可在管道中的下一个

  • 浅谈ASP.NET Core中间件实现分布式 Session

    1.1. 中间件原理 1.1.1. 什么是中间件 中间件是段代码用于处理请求和响应,通常多个中间件链接起来形成管道,由每个中间件自己来决定是否要调用下一个中间件. 1.1.2. 中间件执行过程 举一个示例来演示中间件的执行过程(分别有三个中间件:日志记录.权限验证和路由):当请求进入应用程序时,执行执行日志记录的中间件,它记录请求属性并调用链中的下一个中间件权限验证,如果权限验证通过则将控制权传递给下一个中间件,不通过则设置401 HTTP代码并返回响应,响应传递给日志中间件进行返回. 1.1.

  • ASP.NET Core中间件设置教程(7)

    Asp.Net Core-中间件 在这一章,我们将了解如何设置中间件.中间件技术在 ASP.NET Core中控制我们的应用程序如何响应 HTTP 请求.它还可以控制应用程序的异常错误,这是一个在如何进行身份验证和授权用户执行特定的操作的关键. 中间件是组装成应用的管道来处理请求和响应的软件组件. 每个组件可以选择是否要在管道中将请求传递到下一个组件,并可以在管道中执行某些操作之前和之后的任务. Request委托用于构建请求管道.Request委托用来处理每个HTTP请求. 每件中间件在 AS

随机推荐