.NETCore 之 中间件 01

.Net core 的Pipeline 是由各种各样的中间件组成的(个人理解),通过一张图我们大体认识一样中间件的请求流程。

 

 

 从图中我们可以看出 请求 middleware1->middleware2->middleware,响应:middleware3->middleware2->middleware1,他的请求流程很像  俄罗斯套娃模型

中间件基本使用:

我们在 使用 ApplicationBuilder Use 就可以轻松的注册中间件

  app.Use(next =>
            {
                Console.WriteLine("this is middleware 1");
                return new RequestDelegate(async context =>
                {
                    await context.Response.WriteAsync("this is hello world1 start");
                    await next.Invoke(context);
                    await context.Response.WriteAsync("this is hello world1 end");
                });
            });

            app.Use(next =>
            {
                Console.WriteLine("this is middleware 2");
                return new RequestDelegate(async context =>
                {
                    await context.Response.WriteAsync("this is hello world2 start");
                    await next.Invoke(context);
                    await context.Response.WriteAsync("this is hello world2 end");
                });
            });

            app.Use(next =>
            {
                Console.WriteLine("this is middleware 3");
                return new RequestDelegate(async context =>
                {
                    await context.Response.WriteAsync("this is hello world3 start"); 
                    await context.Response.WriteAsync("this is hello world3 end");
                });
            });

上段代码相应结果:

 

cmd 下:

 

 

 浏览器下:

 

 

我们发现在 控制台显示的是 Middleware3->middleware2->middleware1

但是在浏览器中显示的是 middleware1->middleware2->middleware3     middleware3->middleware2->middleware1

这时候我们求助源码:

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Internal;

namespace Microsoft.AspNetCore.Builder
{
    public class ApplicationBuilder : IApplicationBuilder
    {
        private const string ServerFeaturesKey = "server.Features";
        private const string ApplicationServicesKey = "application.Services";

        private readonly IList<Func<RequestDelegate, RequestDelegate>> _components = new List<Func<RequestDelegate, RequestDelegate>>();

        public ApplicationBuilder(IServiceProvider serviceProvider)
        {
            Properties = new Dictionary<string, object>(StringComparer.Ordinal);
            ApplicationServices = serviceProvider;
        }

        public ApplicationBuilder(IServiceProvider serviceProvider, object server)
            : this(serviceProvider)
        {
            SetProperty(ServerFeaturesKey, server);
        }

        private ApplicationBuilder(ApplicationBuilder builder)
        {
            Properties = new CopyOnWriteDictionary<string, object>(builder.Properties, StringComparer.Ordinal);
        }

        public IServiceProvider ApplicationServices
        {
            get
            {
                return GetProperty<IServiceProvider>(ApplicationServicesKey);
            }
            set
            {
                SetProperty<IServiceProvider>(ApplicationServicesKey, value);
            }
        }

        public IFeatureCollection ServerFeatures
        {
            get
            {
                return GetProperty<IFeatureCollection>(ServerFeaturesKey);
            }
        }

        public IDictionary<string, object> Properties { get; }

        private T GetProperty<T>(string key)
        {
            object value;
            return Properties.TryGetValue(key, out value) ? (T)value : default(T);
        }

        private void SetProperty<T>(string key, T value)
        {
            Properties[key] = value;
        }

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

        public IApplicationBuilder New()
        {
            return new ApplicationBuilder(this);
        }

        public RequestDelegate Build()
        {
            RequestDelegate app = context =>
            {
                // If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened.
                // This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware.
                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);
                }

                context.Response.StatusCode = StatusCodes.Status404NotFound;
                return Task.CompletedTask;
            };

            foreach (var component in _components.Reverse())
            {
                app = component(app);
            }

            return app;
        }
    }
}

app.Use  主要把 中间件记录下来,系统会自动掉调用 Build 方法执行,调用Build 之后。按照上面的代码 在控制台会显示   Middleware3->middleware2->middleware1,其次最终返回的中间件是middleware1 ,这时候 系统会 Kestrel 服务器会从上到下执行中间件

 

方便理解原来,模仿了 ApplicationBuilder 注册流程。

  public class ApplicationBuilder
    {
        private readonly Stack<Func<RequestDelegate, RequestDelegate>> _components = new Stack<Func<RequestDelegate, RequestDelegate>>();

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

        public RequestDelegate Build()
        {
            RequestDelegate app = async s =>
            {
                Console.WriteLine("http 404");
                await Task.CompletedTask;
            };

            while (this._components.Any())
            {
                app = this._components.Pop()(app);
            }
            return app;
        }
    }
public delegate Task RequestDelegate(string context); 

 

调用:

   ApplicationBuilder app = new ApplicationBuilder();

            app.Use(next => {
                Console.WriteLine("this is middleware 1");
                return new RequestDelegate(async context => {
                    Console.WriteLine("this is hello world 1 start");
                    await next.Invoke(context);
                    Console.WriteLine("this is hello world 1 end");
                });
            });

            app.Use(next => {
                Console.WriteLine("this is middleware 2");
                return new RequestDelegate(async context => {
                    Console.WriteLine("this is hello world 2 start");
                    await next.Invoke(context);
                    Console.WriteLine("this is hello world 2 end");
                });
            });

            app.Use(next => {
                Console.WriteLine("this is middleware 3");
                return new RequestDelegate(async context => {
                    Console.WriteLine("this is hello world 3 start"); 
                    Console.WriteLine("this is hello world 3 end");

                    await Task.CompletedTask;
                });
            });


            app.Build();

 

 

至于后面的操作是 Kestrel 服务器干的事情了。

  

 

 

 

 

posted @ 2020-06-13 21:37  delaywu  阅读(203)  评论(0编辑  收藏  举报