Middleware的艺术
定义
Middleware直译叫中间件,目前在百度上很难找到一个简单明了的含义解释,.Net下以前也比较难以看到它的身影,但在Microsoft.Owin里,多个地方都看到MiddleWare,我近来在尝试理解Middleware,并在实际中模仿应用,本文章将我的个人理解和大家分享一下。
Middleware的抽象
Microsoft.Owin的Middleware
public abstract class OwinMiddleware { protected OwinMiddleware(OwinMiddleware next); protected OwinMiddleware Next { get; set; } public abstract Task Invoke(IOwinContext context); }
微软把Middleware定义在抽象类,除了Invoke的中间件执行入口,还有一个Next的中间件属性,两者都是非常抽象。Invoke正是本中间件的处理逻辑,参数IOwinContext包含问题试卷和答题卡,Next表示下一个中间件,如果本中间件不想解决此问题,那么就让Next来解决这个问题,以此类推。提问题的人不用关注是谁回答了问题,他只关注答案了够了。
简单的Middleware接口
如果我们把中间件定义为接口,那么一个简单的中间件接口可以如下:
public interface IMiddleware { IMiddleware Next { set; } Answer GetAnswer(Question question); }
当然,在一些实际项目中,中间件的抽象程度不一样,所以中间件的执行方法是根据实际需要作合理的设计,但Next一般不会有变化,它都是代表下一个中间件。
Middleware和插件的区别
插件是应用程序的功能扩展,它可以只关注自身的功能的实现,它可能不是带问题的,但各插件的执行接口是一样的,最简单的插件接口可以如下:
public interface IPlug { // 加载执行插件 void Init(); }
Middleware是问题解决单元抽象,它往往是由多个单元同时协同工作,各单元虽然没有直接关系,但Next属性还是得到了下个中间件的实例,提供给本中间件来传递。Middlware随着数量的增多,解决的问题各类也可以越多,和插件越多功能越丰富有相似。
Middleware的使用场景
我曾经写过《.Net下一个类型转换神器》,近期在代码重构,发现这玩意很适合使用中间件来实现。在类型转换中,调用者想要的是这种:
/// <summary> /// 将value转换为目标类型 /// </summary> /// <param name="value">要转换的值</param> /// <param name="targetType">转换的目标类型</param> /// <returns></returns> object Convert(object value, Type targetType);
value和targetType千变万化,一口可吃不完,本着单一职责原则,我们应该要写很多转换单元,每个单元只负责一种类型转换,转换器由N多个转换单元组成,如果转换单元是中间件,抽象的中间件接口应该如下:
/// <summary> /// 定义类型转换单元的接口 /// </summary> public interface IConvertMiddleware { /// <summary> /// 设置下一个转换单元 /// </summary> IConvertMiddleware Next { set; } /// <summary> /// 将value转换为目标类型 /// </summary> /// <param name="上下文">context</param> /// <returns></returns> object Convert(ConvertContext context); }
实际当中,我们可能未必需要这么抽象,我的实际接口是
/// <summary> /// 定义类型转换单元的接口 /// </summary> public interface IConvert { /// <summary> /// 设置转换器 /// </summary> Converter Converter { set; } /// <summary> /// 设置下一个转换单元 /// </summary> IConvert NextConvert { set; } /// <summary> /// 将value转换为目标类型 /// </summary> /// <param name="value">要转换的值</param> /// <param name="targetType">转换的目标类型</param> /// <returns></returns> object Convert(object value, Type targetType); }
然后,我们来一个一个地来实现转换单元:
某个单元的实现如下:
/// <summary>
/// 表示不作转换的转换单元
/// </summary>
public class NoConvert : IConvert
{
/// <summary>
/// 转换器实例
/// </summary>
public Converter Converter { get; set; }
/// <summary>
/// 下一个转换单元
/// </summary>
public IConvert NextConvert { get; set; }
/// <summary>
/// 将value转换为目标类型
/// </summary>
/// <param name="value">要转换的值</param>
/// <param name="targetType">转换的目标类型</param>
/// <returns></returns>
public object Convert(object value, Type targetType)
{
if (targetType == typeof(object))
{
return value;
}
if (value != null && targetType == value.GetType())
{
return value;
}
return this.NextConvert.Convert(value, targetType);
}
}
NoConvert
更广泛的Middleware
我近来在开发开源项目《NetworkSocket》,自从看到Microsoft.Owin之后的中间件之后,觉得Middleware可以在很多领域中使用,现在这个项目里的协议解析实现者都已经完全由中间件来开发,功能越来丰富,但复杂度感觉没有增大。看看下面代码,你应该能感觉到Middleware的甜味。
var listener = new TcpListener(); listener.Use<HttpMiddleware>(); listener.Use<JsonWebSocketMiddleware>(); listener.Use<FastMiddleware>(); listener.Start(1212);