• <sup id="mk476"></sup>
    <dl id="mk476"></dl>
  • <progress id="mk476"><tr id="mk476"></tr></progress>
    <div id="mk476"><tr id="mk476"></tr></div>
    <sup id="mk476"><ins id="mk476"></ins></sup>
  • <progress id="mk476"></progress>
    <div id="mk476"></div>
    <div id="mk476"><tr id="mk476"></tr></div>
  • <div id="mk476"></div>
    <dl id="mk476"><s id="mk476"></s></dl><dl id="mk476"></dl><div id="mk476"></div>
  • <div id="mk476"></div>
    <dl id="mk476"><ins id="mk476"></ins></dl>

    ASP.NET Core Middleware

    中間件(Middleware)是ASP.NET Core中的一個重要特性。所謂中間件就是嵌入到應用管道中用于處理請求和響應的一段代碼。ASP.NET Core Middleware可以分為兩種類型:

    Conventional Middleware

    這種中間件沒有實現特定的接口或者繼承特定類,它更像是Duck Typing (你走起路來像個鴨子, 叫起來像個鴨子, 那么你就是個鴨子)。有兩種表現形式:

    匿名方法

    這種方式又稱為內聯中間件(in-line middleware),可以使用RunMapUse,MapWhen等擴展方法來實現。如:

    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的擴展方法:RunMapMapWhen
    Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware),最終都會調用IApplicationBuilder接口中的Use(Func<RequestDelegate, RequestDelegate> middleware)方法來實現向請求處理管道中注入中間件,后面會對源碼做分析。

    自定義中間件類

    這種形式利于代碼的復用,如:

    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>();
        }
    }

     

    將自定義中間件配置到請求處理管道中

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseXfhMiddleware();
    }

    IMiddleware

    IMiddleware提供了強類型約束的中間件,其默認實現是MiddlewareFactory,接口定義如下:

    public interface IMiddleware
    {
        Task InvokeAsync(HttpContext context, RequestDelegate next);
    }

    IMiddlewareFactory用于創建IMiddleware實例及對實例進行回收,接口定義:

    public interface IMiddlewareFactory
    {
        public IMiddleware Create (Type middlewareType);
        
        public void Release (IMiddleware middleware);
    }

     

    自定義IMiddleware類型中間件

    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>();
        }
    }

     

    將中間件注入到請求處理管道:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMyMiddleware();
    }

     

    使用IMiddleware類型的中間件需要在容器中進行注冊,否則拋異常,具體原因下面分析:

     

     

    將中間件注入到容器中:

    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可以接受多個參數

    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方法的具體實現,由于該方法代碼量較大,所以這里只看其中的關鍵部分,方法整體流程如下:

    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對象

    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實例是從容器中獲取的,若容器中找不到則會拋出異常:

    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的公共實例方法

    // 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

    var methodInfo = invokeMethods[0];
    if (!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)));
    }

     

    對于InvokeInvokeAsync僅包含一個HttpContext類型參數的情況用到了反射(ActivatorUtilities.CreateInstance方法中)來構建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, middlewa
    if (parameters.Length == 1)
    {
        return (RequestDelegate) methodInfo.CreateDelegate(typeof(RequestDelegate), in
    }

     

    對于包含多個參數的情況,則使用了表達式樹來構建RequestDelegate

    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類的實現如下:

    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類型的委托,請求處理管道其實是一個委托列表,請求委托簽名如下:

    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 -- 預備知識

    posted @ 2018-09-22 17:56 雪飛鴻 閱讀(...) 評論(...) 編輯 收藏

    版權聲明

    本文為作者原創,版權歸作者雪飛鴻所有。 轉載必須保留文章的完整性,且在頁面明顯位置處標明原文鏈接。

    如有問題, 請發送郵件和作者聯系。

    江苏11选5软件