为什么应该在业务层实现管道模式,而不用 ASP.NET Core Middleware 实现

前言

ASP.NET CoreMiddleware(中间件)使用了管道模式:

Request (请求)在管道中传递,依次经过管道中的每一个 MiddleWare 进行处理。

MiddleWare 就像一层层的“滤网”,过滤所有的请求和响应。

这种模式的好处在于,分离关注点。比如验证用户、记录访问日志,可以将这些任务分解到不同 MiddleWare 中,互相解耦。

既然 ASP.NET Core 已经实现了管道模式,为什么还要在业务层实现一遍呢?

原因

我觉得可以分为 2 方面考虑:

  • 与顶级框架解耦,不能保证应用程序代码始终运行在 ASP.NET Core 下,如果将业务迁移到 WPF,记录访问日志这类放在管道中的功能还是需要实现
  • 同时支持不同来源请求,请求可能来自于 Web API,也可能来自于 Windows Service 定时调用,但业务层处理逻辑应保持一样

因此,ASP.NET Core 框架应该只用于接收输入和返回输出,而在业务层实现管道模式。

定义 API 的最佳实践

这里使用了 MediatR 类库实现 Controller 方法,将业务逻辑和 Controller 进行隔离。

示例代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
[HttpGet]
public async Task<string> Demo([FromQuery] DemoQuery request)
{
    return await _mediator.Send(request);
}

public class DemoQuery : IRequest<string>
{
    public string Name { get; set; }
}

public class DemoQueryHandler : IRequestHandler<DemoQuery, string>
{ 
    public async Task<string> Handle(DemoQuery request, CancellationToken cancellationToken)
    {
        Console.WriteLine("DemoQueryHandler执行");
        return request.Name;
    }
}

MediatR 不仅仅是一个简单的中介模式实现,它还提供了 Behaviors 的概念。

Behaviors 非常类似于 ASP.NET Core 中的 MiddleWare,可以让我们实现管道模式。

管道实现

要定义 Behaviors,我们需要实现 IPipelineBehavior 接口,示例代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class FirstPipelineBehavior<TRequest, TResponse>
    : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        Console.WriteLine("FirstPipelineBehavior执行中");

        var response = await next();

        Console.WriteLine("FirstPipelineBehavior执行完成");

        return response;
    }
}

MiddleWare 类似,调用 next() 可以将请求向下传递。

接着在 Startup.cs 中注册管道:

1
2
3
4
services.AddMediatR(typeof(Startup));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(FirstPipelineBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(SecondPipelineBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ThirdPipelineBehavior<,>));

访问 API 可以看到,管道可以在 IRequestHandler 执行之前和之后执行代码,并且处理顺序和注册顺序相同:

0%