ASP.NET Core 1.0中的管道-中間件模式

ASP.NET Core 1.0借鑑了Katana項目的管道設計(Pipeline)。日誌記錄、用戶認證、MVC等模塊都以中間件(Middleware)的方式註冊在管道中。顯而易見這樣的設計很是鬆耦合而且很是靈活,你能夠本身定義任意功能的Middleware註冊在管道中。這一設計很是適用於「請求-響應」這樣的場景——消息從管道頭流入最後反向流出。html

在本文中暫且爲這種模式起名叫作「管道-中間件(Pipeline-Middleware)」模式吧。git

本文將描述」管道-中間件模式」的「契約式」設計和「函數式」設計兩種方案。app

1、什麼是管道-中間件模式?函數

在此模式中抽象了一個相似管道的概念,全部的組件均以中間件的方式註冊在此管道中,當請求進入管道後:中間件依次對請求做出處理,而後從最後一箇中間件開始處理響應內容,最終反向流出管道。測試

2、契約式設計ui

契約式設計是從面向對象的角度來思考問題,根據管道-中間件的理解,中間件(Middleware)有兩個職責:this

    public interface IMiddleware
    {
        Request ProcessRequest(Request request);
        Response ProcessResponse(Response response);
    }

管道(Pipeline)抽象應該可以註冊中間件(Middleware):.net

    public interface IApplicationBuilder
    {
  
        void Use(IMiddleware middleware);

        void UseArrange(List<IMiddleware> middlewares);

        Context Run(Context context);
    }

實現IApplicationBuilder:設計

    public class ApplicationBuilder : IApplicationBuilder
    {
        public IWindsorContainer Container { get; private set; }
        private readonly List<IMiddleware> _middlewares;

        public ApplicationBuilder(IWindsorContainer container)
        {
            Contract.Requires(container!=null,"container!=null");

            _middlewares=new List<IMiddleware>();
            Container = container;
        }

        public void Use(IMiddleware middleware)
        {
            Contract.Requires(middleware != null, "middleware!=null");

            _middlewares.Add(middleware);
        }

        public void UseArrange(List<IMiddleware> middlewares)
        {
            Contract.Requires(middlewares != null, "middlewares!=null");

            _middlewares.AddRange(middlewares);
        }

        public Context Run(Context context)
        {
            Contract.Requires(context!=null,"context!=null");

            var request=context.Request;
            var response=context.Response;

            foreach (var middleware in _middlewares)
            {
                request = middleware.ProcessRequest(request);
            }

            _middlewares.Reverse();

            foreach (var middleware in _middlewares)
            {
                response = middleware.ProcessResponse(response);
            }

            return new Context(request,response);
        }
    }

Run()方法將依次枚舉Middleware並對消息的請求和響應進行處理,最後返回最終處理過的消息。日誌

接下來須要實現一個Middleware:

   public class DefaultMiddleware:IMiddleware
    {
        public Request ProcessRequest(Request request)
        {
            request.Process("default request", "processed by defaultMiddleware");
            return request;
        }

        public Response ProcessResponse(Response response)
        {
            response.Process("default response", "processed by defaultMiddleware");
            return response;
        }
    }

爲了將Middleware註冊進管道,咱們還能夠寫一個擴展方法增長代碼的可讀性:

        public static void UseDefaultMiddleware(this IApplicationBuilder applicationBuilder)
        {
            applicationBuilder.Use<DefaultMiddleware>();
        }

        public static void Use<TMiddleware>(this IApplicationBuilder applicationBuilder)
            where TMiddleware:IMiddleware
        {
            var middleware = applicationBuilder.Container.Resolve<TMiddleware>();

            applicationBuilder.Use(middleware);
        }

寫個測試看看吧:

寫第二個Middleware:

    public class GreetingMiddleware:IMiddleware
    {
        public Request ProcessRequest(Request request)
        {
            request.Process("hello, request","processed by greetingMiddleware");

            return request;
        }

        public Response ProcessResponse(Response response)
        {
            response.Process("hello, request", "processed by greetingMiddleware");

            return response;
        }
    }

編寫測試:

3、函數式設計方案

此方案也是Owin和ASP.NET Core採用的方案,若是站在面向對象的角度,第一個方案是很是清晰的,管道最終經過枚舉全部Middleware來依次處理請求。

站在函數式的角度來看,Middleware能夠用Func<Context, Context>來表示,再來看看這張圖:

一個Middleware的邏輯能夠用Func<Func<Context, Context>, Func<Context, Context>>來表示,整個Middleware的邏輯能夠用下面的代碼描述:

        public Func<Func<Context, Context>, Func<Context, Context>> Process()
        {
            Func<Func<Context, Context>, Func<Context, Context>> middleware = next =>
            {
                Func<Context, Context> process = context =>
                {
                    /*process request*/
                  
                    next(context);

                    /*process response*/

                    return context;
                };

                return process;
            };

            return middleware;
        }

這一過程是理解函數式方案的關鍵,全部Middleware能夠聚合爲一個Func<Context,Context>,爲了易於閱讀,咱們能夠定義一個委託:

public delegate Context RequestDelegate(Context context);

給定初始RequestDelegate,聚合全部Middleware:

        public IApplication Build()
        {
            RequestDelegate request = context => context;

            _middlewares.Reverse();

            foreach (var middleware in _middlewares)
            {
                request = middleware(request);
            }

            return new Application(request);
        }

自定義一個函數式Middleware:

    public class DefaultMiddleware:IMiddleware
    {
        public Func<RequestDelegate, RequestDelegate> Request()
        {
            Func<RequestDelegate, RequestDelegate> request = next =>
            {
                return context =>
                {
                    context.Request.Process("default request", "processed by defaultMiddleware");

                    next(context);

                    context.Response.Process("default response", "processed by defaultMiddleware");

                    return context;
                };

            };

            return request;
        }
    }

全部代碼提供下載:https://git.oschina.net/richieyangs/Pipeline.Middleware.git

相關文章
相關標籤/搜索