netcore mvc 的簡單實現
阿新 • • 發佈:2019-09-16
實現的功能
- 簡單的路由系統
- 支援中介軟體
- 簡單Filter支援
- 只支援HttpPost、HttpGet
- 使用Dotliquid做為檢視渲染引擎
核心實現
HttpChannel
- 複製監聽Tcp請求,並按照Http協議將tcp資料傳輸解析為HttpRequest資料例項,解析完呼叫HttpHandler繼續下一步處理
HttpRequest
- 單次Http請求資訊,包含 HttpMethod、Version、Url、HttpHeader、Cookies等資訊
HttpResponse
- Http響應資訊
DefaultHttpHandler
實現IHttpHandler,負責Middleware責任鏈的建立、執行
private IMiddleware BuildMiddlewareChain() { var builder = new MiddlewareChainBuilder(); builder.Use(new StaticFileMiddleware()); builder.Use(new MvcMiddleware(route)); return builder.Build(); } public async Task<HttpResponse> ProcessAsync(HttpRequest httpRequest) { var chain = BuildMiddlewareChain(); return await chain.Invoke(httpRequest); }
MvcMiddleware
內建的中介軟體,mvc功能具體實現[FIlter模式實現]
public override async Task<HttpResponse> Invoke(HttpRequest httpRequest) { try { var context = new ActionExecuteContext { HttpRequest = httpRequest }; var (controller, methodInfo, parameter) = route.Route(httpRequest); if (controller == null) { return await HttpResponseHelper.CreateNotFoundResponseAsync(); } context.Controller = controller; context.Action = methodInfo; ((ControllerBase)controller).Request = httpRequest; var filterList = GetFilters(controller, methodInfo); var stack = new Stack<IFilter>(); for (var i = 0; i < filterList.Count; i++) { var filter = filterList[i]; await filter.OnActionExecutingAsync(context); if (context.Final) { return context.HttpResponse; } stack.Push(filter); } await controller.OnActionExecutingAsync(context); if (context.Final) { return context.HttpResponse; } var parameters = new List<object>(); if (parameter != null) { parameters.Add(parameter); } if (methodInfo.ReturnType.IsGenericType) //Task<IActionResult> { var actionResult = await (methodInfo.Invoke(controller, parameters.ToArray()) as Task<IActionResult>); context.HttpResponse = await actionResult.ExecuteResultAsync(); } else { var actionResult = methodInfo.Invoke(controller, parameters.ToArray()) as IActionResult; context.HttpResponse = await actionResult.ExecuteResultAsync(); } context.HttpResponse.Cookies.AddRange(controller.ResponseCookie); await controller.OnActionExecutedAsync(context); if (context.Final) { return context.HttpResponse; } while (stack.Count != 0) { var filter = stack.Pop(); await filter.OnActionExecutedAsync(context); if (context.Final) { return context.HttpResponse; } } return context.HttpResponse; } catch (Exception e) { return await HttpResponseHelper.CreateDefaultErrorResponseAsync(e); } }
EasyRoute
實現最簡單的路由解析【URL---> IController/Method】
Controller例項化使用netcore自帶的IOC框架實現
public (IController, MethodInfo, object) Route(HttpRequest request) { var path = request.AbsolutePath; Type controllerType; MethodInfo methodInfo; switch (request.HttpMethod) { case HttpMethod.Get: if (httpGetRoutes.ContainsKey(path)) { (controllerType, methodInfo) = httpGetRoutes[path]; } else { return (null, null, null); } break; case HttpMethod.Post: if (httpPostRoutes.ContainsKey(path)) { (controllerType, methodInfo) = httpPostRoutes[path]; } else { return (null, null, null); } break; default://Unsupport httpmethod return (null, null, null); } var controllerObj = ServiceLocator.Instance.GetService(controllerType) as IController; //var controllerObj = Activator.CreateInstance(controllerType) as IController; object parameter = null; var parameterType = methodInfo.GetParameters().SingleOrDefault()?.ParameterType; if (parameterType != null) { parameter = ResolveParameter(parameterType, request); } return (controllerObj, methodInfo, parameter); }
IActionResult
執行結果的抽象,類比MVC的IActionResult
內建型別
HttpStatusCodeResult:只返回特定StatusCode,沒有具體內容
JsonResult:返回JSON結果,content-type:application/json
RedirectResult:返回302
ViewResult:使用DotLiquid作為檢視渲染引擎
public async Task<HttpResponse> ExecuteResultAsync() { var absoluteName = $"Views/{ViewName}"; Template template; if (viewCache.ContainsKey(absoluteName)) { template = viewCache[absoluteName]; } else { var templateStr = Template.FileSystem.ReadTemplateFile(new Context(CultureInfo.CurrentCulture), absoluteName); template = Template.Parse(templateStr); viewCache.Add(absoluteName, template); } var content = template.Render(Hash.FromAnonymousObject(ViewData)); var res = new HttpResponse(); await res.WriteBodyAsync(Constants.DefaultEncoding.GetBytes(content)); return res; }
IController
- mvc控制器介面
HttpCookie
- cookie操作類
IFilter
過濾器介面
public interface IFilter { int Order { get; set; } Task OnActionExecutingAsync(ActionExecuteContext context); Task OnActionExecutedAsync(ActionExecuteContext context); }
其他
- 本專案只為學習使用,如有錯誤,請指出
- 本專案為另一個專案 EasyProxy 的附屬產物,所以沒有獨立的github倉庫,具體目錄為 HttpServer