传送门
既然希望业务代码在不同的服务器宿主环境下执行,那么就需要抽象不同服务器宿主之间的行为差异,解耦服务器宿主与业务代码之间的关系
概要设计
无论是任何服务器宿主(host)与客户端(client)的交互都摆脱不了
Request → Process → Response
的套路,区别在于不同的 Host 产生的 Request 不同,需要返回的 Response 也不一样;
至于Process,在.NET中无非就是
Instance + Properties + Arguments + Method + Result
例如:
var method = FindMethod();
var instance = CreateInstance(method.ReflectedType);
var props = GetProperties();
instance.SetProps(props);
var arguments = GetArguments();
var result = method.invoke(instance,arguments);
所以我需要一个对象用于获得 Process 五件套参数,我决定抽象一个 IRequest 对象,IRequest对象就包含了刚才例子中的 FindMethod,GetProperties,GetArguments几个方法;
这样我就可以简单的实现一个通用的 Process 方法了
public object Process(IRequest request)
{
var request = GetRequest();
var method = request.FindMethod();
var instance = Activator.CreateInstance(method.ReflectedType);
var props = request.GetProperties();
foreach(var prop in props)
{
method.ReflectedType.GetProperty(prop.Name).SetValue(instance, prop.Value);
}
var arguments = request.GetArguments();
var result = method.invoke(instance, arguments);
return result;
}
好吧,我知道这代码看起来容错性非常差,但不必在意,就当这只是在画草图。
有了这样一个通用的 Process 方法,那么只需要在不同的服务器宿主环境下构建一个 IRequest 就可以了,把这个工作交给适配器(Adapter)去做,所以这个流程看上去就这样的:
WebRequest → WebAdapter → IRequest → Process → Result → WebAdapter → WebResponse
或是
RpcRequest → RpcAdapter → IRequest → Process → Result → RpcAdapter → RpcResponse
服务器宿主接收到请求之后,通过特定 Adapter 将原本的 Request 转为刚刚自定义的 IRequest 对象,然后通过 Process 方法执行请求,得到返回值之后再次经过 Adapter 的处理,将 object Result 转为指定类型的 Response 返回给客户端;
在MVC下代码大致就是这样的:
internal class MisRouteHandler : IHttpHandler, IRouteHandler
{
private MvcAdapter _adapter;
public MisRouteHandler(string urlTemplate)
{
_adapter = new MvcAdapter(urlTemplate);
}
public bool IsReusable => true;
public void ProcessRequest(HttpContext httpContext)
{
try
{
var request = _adapter.CreateRequest(httpContext);
var result = Mis.Process(request);
var response = _adapter.ConvertToResponse(result);
httpContext.Response.StatusCode = response.StatusCode;
httpContext.Response.ContentType = response.ContentType;
foreach (var header in response.headers)
{
httpContext.Response.Headers.Set(header.name, header.value);
}
if (response.content != null)
{
httpContext.Response.BinaryWrite(response.content);
}
}
catch (Exception e)
{
httpContext.Response.ContentType = "text/plain;charset=utf-8";
httpContext.Response.StatusCode = 500;
httpContext.Response.BinaryWrite(Encoding.UTF8.GetBytes(e.ToString()));
}
}
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) => this;
}
流程图
未完待续……
网友评论