Middleware理解

Applicationbuilder维护着一个中间件集合

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

本质都是是往ApplicationBuilder对象的_components成员里添加中间件Func<RequestDelegate, RequestDelegate>

1
2
3
public delegate Task RequestDelegate(HttpContext context);

private readonly IList<Func<RequestDelegate, RequestDelegate>> _components

通过传值和返回都是RequestDelegate委托,对HttpContext的处理,各个中间件首尾相连,形似管道

管道

Middleware使用

ApplicationBuilder添加中间件

ApplicationBuilder实例方法

1
2
3
4
5
6
7
8
9
10
11
12
namespace Microsoft.AspNetCore.Builder
{
//略

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

//略
}

ApplicationBuilder拓展方法

  • UseMiddleware
1
2
3
4
public static IApplicationBuilder UseMiddleware<TMiddleware>(this IApplicationBuilder app, params object[] args)
{
return app.UseMiddleware(typeof(TMiddleware), args);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public static IApplicationBuilder UseMiddleware<TMiddleware>(this IApplicationBuilder app, Type middleware, params object[] args)
{
if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo()))
{
//省略校验

//调用接口方法实现
return UseMiddlewareInterface(app, middleware);
}

var applicationServices = app.ApplicationServices;
return app.Use(next =>
{
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();
//省略一堆校验和数据拼装
var methodInfo = invokeMethods[0];
//省略校验
var parameters = methodInfo.GetParameters();
//省略校验
//下面是通过Expression树来创建中间件对象
var ctorArgs = new object[args.Length + 1];
ctorArgs[0] = next;
Array.Copy(args, 0, ctorArgs, 1, args.Length);
var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);
if (parameters.Length == 1)
{
return (RequestDelegate)methodInfo.CreateDelegate(typeof(RequestDelegate), instance);
}

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

  • Map
1
2
3
4
public static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, Action<IApplicationBuilder> configuration)
{
return Map(app, pathMatch, preserveMatchedPathSegment: false, configuration);
}

核心方法,通过pathMatch参数判断给中间件加上执行条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, bool preserveMatchedPathSegment, Action<IApplicationBuilder> configuration)
{
//省略一堆校验

// create branch
var branchBuilder = app.New();
configuration(branchBuilder);
var branch = branchBuilder.Build();

var options = new MapOptions
{
Branch = branch,
PathMatch = pathMatch,
PreserveMatchedPathSegment = preserveMatchedPathSegment
};
return app.Use(next => new MapMiddleware(next, options).Invoke);
}

MapMiddleware是对RequestDelegate的进一步封装,增加MapOptions,通过Invoke方法,形成一个中间件

1
Func<RequestDelegate,RequestDelegate> = (RequestDelegate next) = new MapMiddleware(next,options).Invoke
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class MapMiddleware
{
private readonly RequestDelegate _next;
private readonly MapOptions _options;

public async Task Invoke(HttpContext context)
{
//省略校验

if (context.Request.Path.StartsWithSegments(_options.PathMatch, out var matchedPath, out var remainingPath))
{
var path = context.Request.Path;
var pathBase = context.Request.PathBase;

if (!_options.PreserveMatchedPathSegment)
{
// Update the path
context.Request.PathBase = pathBase.Add(matchedPath);
context.Request.Path = remainingPath;
}

try
{
await _options.Branch(context);
}
finally
{
if (!_options.PreserveMatchedPathSegment)
{
context.Request.PathBase = pathBase;
context.Request.Path = path;
}
}
}
else
{
await _next(context);
}
}
}
MapWhen

通过WhenMapOptions条件匹配路由

这种方式更具有拓展性,相比较Map判断逻辑写在Invoke方法里,这个可以匹配规则通过传递委托形式自定义

1
2
3
4
public class WhenMapOptions
{
private Func<HttpContext, bool> _predicate;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static IApplicationBuilder MapWhen(this IApplicationBuilder app, Predicate predicate, Action<IApplicationBuilder> configuration)
{
//省略校验

// create branch
var branchBuilder = app.New();
configuration(branchBuilder);
var branch = branchBuilder.Build();

// put middleware in pipeline
var options = new MapWhenOptions
{
Predicate = predicate,
Branch = branch,
};
return app.Use(next => new MapWhenMiddleware(next, options).Invoke);
}
Run

在管道增加终端中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void Run(this IApplicationBuilder app, RequestDelegate handler)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}

if (handler == null)
{
throw new ArgumentNullException(nameof(handler));
}

app.Use(_ => handler);
}

ApplicationBuilder构建管道

通过Build()方法,构建最终管道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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;
}

Middleware用法

  • app.Use()
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Startup
{
public void Configure(IApplicationBuilder app)
{
// Middleware A
app.Use(async (context, next) =>
{
Console.WriteLine("A (in)");
await next();
Console.WriteLine("A (out)");
});
}
}
  • app.UseMiddleware()

有两种方式一种是实现IMiddleware,还有一种是符合中间件规范并提供同步Invoke方法或者异步InvokeAsync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Startup
{
public void Configure(IApplicationBuilder app)
{
// Middleware B
app.UseMiddleware<MiddlewareB>();
}
}

public class MiddlewareB
{
private readonly RequestDelegate _next;

public MiddlewareB(RequestDelegate next)
{
_next = next;
}

public async Task InvokeAsync(HttpContext httpContext)
{
//省略校验
Console.WriteLine("B (in)");
_next.Invoke(httpContext);
Console.WriteLine("B (out)");
}
}
  • app.Map()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Startup
{
public void Configure(IApplicationBuilder app)
{
// Middleware B
app.Map(
new PathString("/foo"),//请求/foo才会执行这段
a => a.Use(async (context, next) =>
{
Console.WriteLine("B (in)");
await next();
Console.WriteLine("B (out)");
}));

}
}
  • app.WhenMap()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Startup
{
public void Configure(IApplicationBuilder app)
{
// Middleware B
app.UseWhen(
context => context.Request.Path.StartsWithSegments(new PathString("/foo")),//请求/foo才会执行这段
a => a.Use(async (context, next) =>
{
Console.WriteLine("B (in)");
await next();
Console.WriteLine("B (out)");
}));
}
}
  • app.Run()
1
2
3
4
5
6
7
8
9
10
11
12
public class Startup
{
public void Configure(IApplicationBuilder app)
{
// Middleware C
app.Run(async context =>
{
Console.WriteLine("C");
await context.Response.WriteAsync("Hello World from the terminal middleware");
});
}
}

官方中间件

  • HealthCheckMiddleware
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class HealthCheckMiddleware
{
private readonly RequestDelegate _next;
private readonly HealthCheckOptions _healthCheckOptions;
private readonly HealthCheckService _healthCheckService;

public HealthCheckMiddleware(
RequestDelegate next,
HealthCheckOptions healthCheckOptions,
HealthCheckService healthCheckService
)
{
//省略校验

_next = next;
_healthCheckOptions = healthCheckOptions;
_healthCheckService = healthCheckService
}

public async Task InvokeAsync(HttpContext httpContext)
{
//省略
}
}

本质上跟MapMiddleware实现是一样的,包括自定义中间件也可以这样实现