分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > IT知识

ASP.NET Core学习总结(1)

发布时间:2023-09-06 01:55责任编辑:彭小芳关键词:.NET

  经过那么长时间的学习,终于想给自己这段时间的学习工作做个总结了。记得刚开始学习的时候,什么资料都没有,光就啃文档。不过,值得庆幸的是,自己总算还有一些Web开发的基础。至少ASP.NET的WebForm和MVC那一套还是有所了解的,虽然也不是很精通。说起来,那时候对整个网络应用的整体流程以及什么HTTP协议都不是很了解。终归是在微软爸爸的庇护下艰难的成长。

1、概念

  概念这种东西,感觉还是太过于学术化。也就是时间长了,慢慢就能理解的一些经常用到的词而已。对于大多数人来说,我们几乎每天都会浏览网页。也许,我们对于网络应用的基本认识,就是从这里开始的。可惜,很多人的认识仍然停留在打开浏览器看网页上。以至于,对于网页是怎么来的,怎么呈现的毫无概念。

  网络应用是一种分布式系统,通常由客户端和服务端组成。通过HTTP协议进行通信,是一种请求/应答模式。浏览器通常作为客户端,而我们开发的Web应用,通常作为服务端。

2、原理

  上面这张图来源于微软的官方文档,它简单直观的描述了我们将要开发的Web应用的基本原理。首先,ASP.NET Core application代表了我们的整个Web应用,它通过HTTP协议与外部进行通信。而在我们程序的内部,首先就是由ASP.NET Core的框架所支配的。Kestrel是一个可以监听和响应请求的底层服务,它会把接收到的HTTP报文封装成HttpContext传递给我们的应用程序代码。同时,把应用程序处理好的响应转换为响应报文返回给客户端。

? 现在,让我们深入应用程序代码的内部。应用程序管道,本质上是由一个委托链构成的。这个名为RequestDelegate的委托有两个参数,第一个是httpContext,第二个是next,类型也是RequestDeletage,指向下一个委托。

  

  由上图我们看到,请求和响应实质上是由一系列中间件处理共同处理的。而事实上,这些中间件最终会编译为一个委托链(所有Middleware类按照约定都应该包含一个Invoke方法和构造函数,构造函数中包含了next,Invoke中包含了httpContext)。总结来说,当请求进来以后,首先会执行第一个委托。而第一个委托的内部可以选择是否调用下一个委托。如同上图所示,如果第一中间件,实质上会变成一个委托,不调用next()。那么,请求便在该中间件短路了,即请求不再向下传递,而是直接返回响应了。

  接下来,我们来介绍ASP.NET Core框架的核心部分,即MVC。对于每一个请求来说,应该都会有一个对应的URL。而我们的程序通常也会有一个对应的处理方法,即我们的控制器动作。现在,框架所解决的第一个问题即是,如何根据URL映射到对应的处理方法,即路由机制。

  路由机制是由Microsoft.AspNetCore.Routing实现的。它最核心的部分是RouterMiddleware中的那段代码。

 ???????public async Task Invoke(HttpContext httpContext) ???????{ ???????????var context = new RouteContext(httpContext); ???????????context.RouteData.Routers.Add(_router);//_router是通过依赖注入,从服务容器中获得的 ?????????????//这一步是最重要的,它会根据RouteContext寻找一个合适的Handler ?????????????//也就是说,整个路由匹配在于这一步是如何实现的 ???????????await _router.RouteAsync(context); ???????????if (context.Handler == null) ???????????{ ???????????????_logger.RequestDidNotMatchRoutes(); ???????????????await _next.Invoke(httpContext); ???????????} ???????????else ???????????{ ???????????????httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature() ???????????????{ ???????????????????RouteData = context.RouteData, ???????????????}; ???????????????await context.Handler(context.HttpContext); ???????????} ???????}

  上面这个_router是一个IRouter接口类型的变量。实质上,当我们在注册MVC服务的时候,已经添加了实现类。如下所示:

 ???????????// ???????????// Route Handlers ???????????// ???????????services.TryAddSingleton<MvcRouteHandler>(); // Only one per app ???????????services.TryAddTransient<MvcAttributeRouteHandler>(); // Many per app
 ???????????var routes = new RouteBuilder(app) ???????????{ ???????????????DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(), ???????????}; ???????????configureRoutes(routes); ???????????routes.Routes.Insert(0,AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices)); ???????????return app.UseRouter(routes.Build());

  我们说,MvcRouteHandler和MvcAttributeRouteHandler都实现了IRouter接口。第一部分的代码的意图在于将这两个Handler添加到服务容器。而第二部分代码的意图在于将它们从服务容器中取出,传递给路由中间件。

? 那么,问题在于,MvcRouteHandler和MvcAttributeRouteHandler是如何实现的?首先,我们来看看MvcRouteHandler内部是如何实现的。

public Task RouteAsync(RouteContext context) ???????{ ???????????if (context == null) ???????????{ ???????????????throw new ArgumentNullException(nameof(context)); ???????????} ?????????????//海选 ???????????var candidates = _actionSelector.SelectCandidates(context); ???????????if (candidates == null || candidates.Count == 0) ???????????{ ???????????????_logger.NoActionsMatched(context.RouteData.Values); ???????????????return Task.CompletedTask; ???????????} ?????????????????????????//精选 ???????????var actionDescriptor = _actionSelector.SelectBestCandidate(context, candidates); ???????????if (actionDescriptor == null) ???????????{ ???????????????_logger.NoActionsMatched(context.RouteData.Values); ???????????????return Task.CompletedTask; ???????????} ?????????????//使用lambda表达式编译成RequestDelegate ???????????context.Handler = (c) => ???????????{ ???????????????var routeData = c.GetRouteData(); ???????????????var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor); ???????????????if (_actionContextAccessor != null) ???????????????{ ???????????????????_actionContextAccessor.ActionContext = actionContext; ???????????????} ???????????????var invoker = _actionInvokerFactory.CreateInvoker(actionContext); ???????????????if (invoker == null) ???????????????{ ???????????????????throw new InvalidOperationException( ???????????????????????Resources.FormatActionInvokerFactory_CouldNotCreateInvoker( ???????????????????????????actionDescriptor.DisplayName)); ???????????????} ?????????????????//这里才是核心处理部分 ???????????????return invoker.InvokeAsync(); ???????????}; ???????????return Task.CompletedTask; ???????}

  我们来解释一下,关键在于_actionSelector。它会根据routeContext挑选出candidates(候选者),这是第一步。这一步只是筛选出了所有URL匹配的Action,而第二步则需要继续考虑路由约束的问题。如果第二步还是有多个符合条件的Action,则会引发异常。第三步我们看到,利用一个lambda表达式生成RequestDelegate的委托,即一个Handler(前面我们曾看到在路由中间件中调用)。在这个委托中我们需要关注的是invoker,它是一个IActionInvoker类型的变量。它的默认实现通常是ControllerActionInvoker,我们稍后会深入讨论这个类的内部实现。我们看到,invoker是由工厂函数根据actionContext生成的,最终调用了InvokeAsync方法。也就是说,至此为止,我们还是无法得知我们所编写的Action代码是怎样执行的。而为了知道这一点,我们只能继续深入。

  我们看到,invoker是由_actionInvokerFactory创建的。而_actionInvokerFactory是IActionInvokerFactory类型,从服务容器中获取。我们来查找它是怎样注入到容器中的。

 ???????????// ???????????// Action Invoker ???????????// ???????????// The IActionInvokerFactory is cachable ???????????services.TryAddSingleton<IActionInvokerFactory, ActionInvokerFactory>(); ???????????services.TryAddEnumerable( ???????????????ServiceDescriptor.Transient<IActionInvokerProvider, ControllerActionInvokerProvider>());

  可以看到,它注入了一个默认实现,ActionInvokerFactory。它的内部是这样的:

 public IActionInvoker CreateInvoker(ActionContext actionContext) ???????{ ???????????var context = new ActionInvokerProviderContext(actionContext); ???????????foreach (var provider in _actionInvokerProviders) ???????????{ ???????????????provider.OnProvidersExecuting(context); ???????????} ???????????for (var i = _actionInvokerProviders.Length - 1; i >= 0; i--) ???????????{ ???????????????_actionInvokerProviders[i].OnProvidersExecuted(context); ???????????} ???????????return context.Result; ???????}

  事实上,ActionInvokerFactory并没有直接处理,而是交给了IActionInokerProvider。而在上面我们看到它的默认实现是ControllerActionInvokerProvider。

public void OnProvidersExecuting(ActionInvokerProviderContext context) ???????{ ???????????if (context == null) ???????????{ ???????????????throw new ArgumentNullException(nameof(context)); ???????????} ???????????if (context.ActionContext.ActionDescriptor is ControllerActionDescriptor) ???????????{ ???????????????var controllerContext = new ControllerContext(context.ActionContext); ???????????????// PERF: These are rarely going to be changed, so let‘s go copy-on-write. ???????????????controllerContext.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories); ???????????????controllerContext.ModelState.MaxAllowedErrors = _maxModelValidationErrors; ?????????????//缓存策略 ???????????????var cacheResult = _controllerActionInvokerCache.GetCachedResult(controllerContext); ???????????????var invoker = new ControllerActionInvoker( ???????????????????_logger, ???????????????????_diagnosticSource, ???????????????????controllerContext, ???????????????????cacheResult.cacheEntry, ???????????????????cacheResult.filters); ???????????????context.Result = invoker; ???????????} ???????}

 我们最终发现,invoker来源于这里,其中还做了缓存策略。现在,是时候揭开这个ControllerActionInvoker的神秘面纱了。

ASP.NET Core学习总结(1)

原文地址:https://www.cnblogs.com/xsddxz/p/9039253.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved