ASP.NET Core学习总结(1)
经过那么长时间的学习,终于想给自己这段时间的学习工作做个总结了。记得刚开始学习的时候,什么资料都没有,光就啃文档。不过,值得庆幸的是,自己总算还有一些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(,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 == )
{
_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 - ; i >= ; 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)的更多相关文章
- ASP.NET Core学习系列
.NET Core ASP.NET Core ASP.NET Core学习之一 入门简介 ASP.NET Core学习之二 菜鸟踩坑 ASP.NET Core学习之三 NLog日志 ASP.NET C ...
- WebAPI调用笔记 ASP.NET CORE 学习之自定义异常处理 MySQL数据库查询优化建议 .NET操作XML文件之泛型集合的序列化与反序列化 Asp.Net Core 轻松学-多线程之Task快速上手 Asp.Net Core 轻松学-多线程之Task(补充)
WebAPI调用笔记 前言 即时通信项目中初次调用OA接口遇到了一些问题,因为本人从业后几乎一直做CS端项目,一个简单的WebAPI调用居然浪费了不少时间,特此记录. 接口描述 首先说明一下,基于 ...
- ASP.NET Core学习指导
ASP.NET Core 学习指导 "工欲善其事必先利其器".我们在做事情之前,总应该做好充分的准备,熟悉自己的工具.就像玩游戏有一些最低配置一样,学习一个新的框架,也需要有一些基 ...
- Asp.Net Core学习笔记:入门篇
Asp.Net Core 学习 基于.Net Core 2.2版本的学习笔记. 常识 像Django那样自动检查代码更新,自动重载服务器(太方便了) dotnet watch run 托管设置 设置项 ...
- ASP.NET Core学习零散记录
赶着潮流听着歌,学着.net玩着Core 竹子学Core,目前主要看老A(http://www.cnblogs.com/artech/)和tom大叔的博客(http://www.cnblogs.com ...
- ASP.NET Core学习之三 NLog日志
上一篇简单介绍了日志的使用方法,也仅仅是用来做下学习,更何况只能在console输出. NLog已是日志库的一员大佬,使用也简单方便,本文介绍的环境是居于.NET CORE 2.0 ,目前的版本也只有 ...
- ASP.NET Core学习之一 入门简介
一.入门简介 在学习之前,要先了解ASP.NET Core是什么?为什么?很多人学习新技术功利心很重,恨不得立马就学会了. 其实,那样做很不好,马马虎虎,联系过程中又花费非常多的时间去解决所遇到的“问 ...
- Asp.net Core学习笔记
之前记在github上的,现在搬运过来 变化还是很大的,感觉和Nodejs有点类似,比如中间件的使用 ,努力学习ing... 优点 不依赖IIS 开源和跨平台 中间件支持 性能优化 无所不在的依赖注入 ...
- 2019年ASP.NET Core学习路线
- [先决条件] + C# + Entity Framework + ASP.NET Core + SQL 基础知识 - [通用开发技能] + 学习 GIT, 在 GitHub 中创建开源项目 + 掌 ...
随机推荐
- python scikit-learn 安装中的各种事宜
由于兴趣,想安装scikit,但是安装时提示pip版本低,让更新,但是他给的更新命令用了之后并不能更新成功(我是指我的) 网上的各种命令都试过了,弄了大半天还是不行,后来我把SCIKIT换成(whl- ...
- collections系列之Counter
collections模块中有一个叫做Counter的类,该类的作用就是计数器,Counter是对dict的加工,所有Counter继承了dict的方法 1.创建一个Counter,需要import ...
- sign和token设计
签名设计 对于敏感的api接口,需使用https协议 https是在http超文本传输协议加入SSL层,它在网络间通信是加密的,所以需要加密证书. https协议需要ca证书,一般需要交费. 签名的设 ...
- Zabbix监控PostgreSQL
目录 Zabbix监控PostgreSQL 1. 安装libzbxpgsql 2. 配置zabbix配置文件zabbix_agentd.conf 3. 创建监控用户 4. 导入监控模板 5. 主机链接 ...
- 使用Spring+Junit4进行测试
前言 单元测试是一个程序员必备的技能,我在这里就不多说了,直接就写相应的代码吧. 单元测试基础类 import org.junit.runner.RunWith; import org.springf ...
- inux中Vi不能高亮显示行号的解决办法
适用版本:CentOS,RedHat,UBUNTU,Fedora解决办法如下: 在UBUNTU中vim的配置文件存放在/etc/vim目录中,配置文件名为vimrc 在Fedora中vim的配置文件存 ...
- 洛谷 P2986 [USACO10MAR]伟大的奶牛聚集(树形动规)
题目描述 Bessie is planning the annual Great Cow Gathering for cows all across the country and, of cours ...
- wpf 进度条 下拉
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsof ...
- 常用C字符串函数
static void str_repalce(char *src,char *from,char *to) { char *p,*q; int lenFrom; int le ...
- Basic4android v3.80 beta 发布
增加了条件编译,共享模块,部分支持jar 文件直接访问.还有其他一些更新. I'm happy to release B4A v3.80 BETA. This version includes sev ...