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

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

在本文中暫且爲這種模式起名叫做“管道-中間件(Pipeline-Middleware)”模式吧。

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

一、什麼是管道-中間件模式?

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

二、契約式設計

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

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

管道(Pipeline)抽象應該能夠註冊中間件(Middleware):

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

編寫測試:

三、函數式設計方案

此方案也是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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章