一 前言
Artech 分享了 200行代码,7个对象——让你了解ASP.NET Core框架的本质 。 用一个极简的模拟框架阐述了ASP.NET Core框架最为核心的部分。
这里一步步来完成这个迷你框架。
二 先来一段简单的代码
这段代码非常简单,启动服务器并监听本地5000端口和处理请求。
static async Task Main(string[] args)
{
HttpListener httpListener = new HttpListener();
httpListener.Prefixes.Add("http://localhost:5000/");
httpListener.Start();
while (true)
{
var context = await httpListener.GetContextAsync();
await context.Response.OutputStream.WriteAsync(Encoding.UTF8.GetBytes("hello world"));
context.Response.Close();
}
}
现在要分离服务器(Server) 和 请求处理(handle),那么一个简单设计架构就出来了 :
Pipeline =Server + HttpHandler
三 处理器的抽象
处理器要从请求(Request)中获取数据,和定制响应(Response)的数据。
可以想到我们的处理器的处理方法应该是这样的:
Task Handle(/*HttpRequest HttpResponse*/);
它可以处理请求和响应,由于处理可以是同步或者异步的,所以返回Task。
很容易想到要封装http请求和响应,封装成一个上下文(Context) 供处理器使用(这样的好处,处理器需要的其他数据也可以封装在这里,统一使用),所以要开始封装HttpContext。
封装HttpContext
public class HttpRequest
{
public Uri Url { get; }
public Nameva lueCollection Headers { get; }
public Stream Body { get; }
}
public class HttpResponse
{
public Nameva lueCollection Headers { get; }
public Stream Body { get; }
public int StatusCode { get; set; }
}
public class HttpContext
{
public HttpRequest Request { get; set; }
public HttpResponse Response { get; set; }
}
要支持不同的服务器,则不同的服务器都要提供HttpContext,这样有了新的难题:服务器和HttpContext之间的适配 。
现阶段的HttpContext包含HttpRequest和HttpResponse,请求和响应的数据都是要服务器(Server)提供的。
可以定义接口,让不同的服务器提供实现接口的实例:
public interface IHttpRequestFeature
{
Uri Url { get; }
Nameva lueCollection Headers { get; }
Stream Body { get; }
}
public interface IHttpResponseFeature
{
int StatusCode { get; set; }
Nameva lueCollection Headers { get; }
Stream Body { get; }
}
为了方便管理服务器和HttpContext之间的适配,定义一个功能的集合,通过类型可以找到服务器提供的实例
public interface IFeatureCollection:IDictionary<Type,object>
{
}
public static partial class Extensions
{
public static T Get<T>(this IFeatureCollection features)
{
return features.TryGetValue(typeof(T), out var value) ? (T)value : default;
}
public static IFeatureCollection Set<T>(this IFeatureCollection features,T feature)
{
features[typeof(T)] = feature;
return features;
}
}
public class FeatureCollection : Dictionary<Type, object>, IFeatureCollection { }
接下来修改HttpContext,完成适配
public class HttpContext
{
public HttpContext(IFeatureCollection features)
{
Request = new HttpRequest(features);
Response = new HttpResponse(features);
}
public HttpRequest Request { get; set; }
public HttpResponse Response { get; set; }
}
public class HttpRequest
{
private readonly IHttpRequestFeature _httpRequestFeature;
public HttpRequest(IFeatureCollection features)
{
_httpRequestFeature = features.Get<IHttpRequestFeature>();
}
public Uri Url => _httpRequestFeature.Url;
public Nameva lueCollection Headers => _httpRequestFeature.Headers;
public Stream Body => _httpRequestFeature.Body;
}
public class HttpResponse
{
private readonly IHttpResponseFeature _httpResponseFeature;
public HttpResponse(IFeatureCollection features)
{
_httpResponseFeature = features.Get<IHttpResponseFeature>();
}
public int StatusCode
{
get => _httpResponseFeature.StatusCode;
set => _httpResponseFeature.StatusCode = value;
}
public Nameva lueCollection Headers => _httpRespons