.net core——(2) asp .net 流程解读
开始之前
在开始之前先了解下web应用的一些概念。
WEB服务器
通常来说是指一个程序(进程),监听某个端口,处理浏览器的请求,向浏览器回馈一个文档(网页)。
HTTP请求处理流程始于对请求的监听与接收,终于对请求的响应,这两项工作由同一个对象来完成,我们称之为 “服务器(Server)” ——https://www.cnblogs.com/artech/p/asp-net-core-pipeline.html
主机(Host)
Host代表主机(在.net core中就是一个对象),用来宿主(承载)我们应用(一个IHostedService的实现)和生存期管理。
主机持有一些公共资源:依赖注入 (DI)、Logging、Configuration、IHostedService的实现。
启动主机时,它在 DI 容器中找到 IHostedService 的每个实现,然后调用 IHostedService.StartAsync。
在 web 应用中,其中一个 IHostedService 的实现是启动 HTTP 服务器实现的 web 服务。
这里的HTTP服务器默认是Kestrel。即:ASP.NET Core主机启动时,会启动一个HTTP服务器,默认是Kestrel。启动后监听并响应某个端口的HTTP请求。
HostBuilder是Host的工厂,完成主机该有的功能。
在新建一个asp .net core项目后会有一个Program.cs文件,该文件部分代码如下:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
这样就创建好了一个Host。
参考:
https://www.cnblogs.com/jionsoft/p/12154519.html
https://www.cnblogs.com/lonelyxmas/p/11073121.html
应用???
这部分尚不清晰,请跳过这一节!!!
从代码上来说就是一个IHostedService的实现。主机和应用是一对多的关系,多个应用可以共享主机的信息,如:主机的IOC容器、主机的配置。应用配置。应用当然也可以自己去创建自己的IOC根容器和配置对象.
一个ASP.NET Core应用本质上是一个需要长时间运行的服务,开启这个服务是为了启动一个网络监听器。当监听到抵达的HTTP请求之后,该监听器会将请求传递给应用提供的管道进行处理。管道完成了对请求处理之后会生成HTTP响应,并通过监听器返回客户端。
public interface IHostedService
{
Task StartAsync(CancellationToken cancellationToken);
Task StopAsync(CancellationToken cancellationToken);
}
参考:
asp .net core 处理http请求的流程 https://www.cnblogs.com/calvinK/p/6008915.html
Program类 https://www.cnblogs.com/lonelyxmas/p/12688641.html
主机 https://www.cnblogs.com/lonelyxmas/p/12766653.html
管道
接Host那一节,Host被创建后,管道也被创建。来自浏览器的请求会被送到某个端口,“服务器”监听该端口,于是服务器就是第一个获取请求的节点,服务器是整个管道的 “龙头”。
请求抵达后,服务器会接收请求并将其标准化后向管道后续的节点进行转发。在管道中我们能自己定制各种处理请求的中间件。
管道的创建暂时不深入,可以参考 https://www.cnblogs.com/artech/p/how-pipeline-is-built.html
中间件
管道中位于服务器之后的请求处理节点成为“中间件(Middleware)”
中间件有很多功能,比如路由、重定向等,下图是中间件的一个简单图示(实际上这个图是微软介绍中间件顺序的)。
微软对中间件的介绍 https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1
Kestrel
Kestrel发音: ['kestr(ə)l]
这是一个web服务器,来自浏览器的请求率先进入Kestrel然后进入管道。
Kestrel是进程内服务器,以一个包形式提供,自身不能单独运行,必须HOST在一个.NET的WEB应用程序中。它内部封装了对libuv的调用,但不是libuv库简单的封装库。
当然,在请求和Kestrel之间实际上还可以添加反向代理服务器,这里先不讲,后面会说。
疑问:
asp.net core的所有中间件都是进程内中间件?从main启动,所有的中间件都是在Startup类的Configure方法中添加的
参考:
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/servers/?view=aspnetcore-3.1&tabs=linux
https://www.cnblogs.com/Leo_wl/p/8405711.html
创建项目
根据官方文档 https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-3.1&tabs=linux 创建一个简单的项目
创建项目
dotnet new webapp -o aspnetcoreapp
运行
cd aspnetcoreapp
dotnet watch run
Program.cs
创建Host工厂IHostBuilder实例,然后使用该实例的Build().Run()创建Host(主机)。
完整代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace aspnetcoreapp
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
// CreateDefaultBuilder方法
// 创建了WebHostBuilder实例
// WebHostBuilder实例有三个主要功能
// 1、构建了IConfiguration实例和基础环境配置
// 2、构建了IServiceCollection服务,也就是依赖注入的容器
// 3、创建了webhost实例,这个webhost就是我们的接收请求的第一个管道,其中暴露出来的主要方法Build。
// 详情可看源码。
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
配置
- 设置文件,例如 appsettings.json
- 环境变量
- Azure Key Vault
- Azure 应用程序配置
- 命令行参数
- 已安装或已创建的自定义提供程序
- 目录文件
- 内存中的 .NET 对象
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1
Startup.cs
默认有两个方法,一个配置管道另一个添加services到容器,当应用启动的时候,这两个方法被runtime调用。
完整代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace aspnetcoreapp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// 这个方法被runtime调用,使用这个方法添加services到容器,实际上是配置依赖注入
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
// 这个方法被runtime调用,使用这个方法配置HTTP管道
// 配置中间件(middleware)以构建请求处理流水线
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
}
IServiceCollection
依赖注入的容器。这里的命名实在是不吐槽不行,明明就是个依赖注入,非要叫服务,关键很多地方也有不同意义的服务。下图中微软官方文档的“服务生存周期”实际上是注入的变量的生存周期。。。
ConfigServices()
专门配置依赖注入,实际就是个给DI容器注册类型的方法,不管你add啥,都是向容器注册类型,例如微软doc中给出的一个例子,下面就在这个方法中注册了一些需要依赖注入的类型。
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddScoped<IMyDependency, MyDependency>();
services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
// OperationService depends on each of the other Operation types.
services.AddTransient<OperationService, OperationService>();
}
微软介绍: https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/startup?view=aspnetcore-3.1
Configure()
注册中间件,你需要什么中间件,你就在这里注册。我们创建的这个新项目默认给我们注册了一些中间件,看下图
参数IApplicationBuilder是用来构建管道的,管道本质上就是对 HttpContext 的一系列操作,即通过对 Request 的处理,来生成 Reponse。
参考
https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.aspnetcore.builder.iapplicationbuilder?view=aspnetcore-3.1
https://www.cnblogs.com/RainingNight/p/middleware-in-asp-net-core.html
反向代理
可以用来做负载均衡。实际就是一个请求转发,浏览器的请求不是直接到web服务器,而是先到反向代理服务器,然后代理服务器转发到web服务器。“反向”的意思是代理在硬件服务器上,与之相对的“正向”就是代理在客户端上,xx上网的梯子(代理隐藏了请求的发起者)就是个正向代理。
路由
路由就是根据请求的url映射到处理请求的类和方法,以下代码是常规路由的代码片段
routeBuilder.MapRoute("Default", "{controller=Home}/{action=Index}/{id?}");
上面的代码中,我们定义了一个路由正则,告诉 MVC 如何查看 URL 并找到控制器名称和操作名称,其中控制器是 C# 类,操作是该类上的公共方法。
终结点 EndPoint
一个终结点(EndPoint)就是一个处理请求的委托。终结点是一个抽象概念,不止服务于常见的mvc模式。
两个路由相关的中间件
- UseRouting:向中间件管道添加路由匹配。 此中间件会查看应用中已经定义的终结点集,并根据请求选择最佳匹配。
- UseEndpoints:向中间件管道添加终结点执行。 它会运行与所选终结点关联的委托。
路由原理
引用自 https://www.cnblogs.com/RandyField/p/12770992.html
1.【定义EndPoints】:在程序启动前应该定义好程序中有哪些终结点,针对mvc来说的话可以自动将程序中与路由匹配的action转换成对应的终结点,其它框架应该也有对应的方式,反正最终我们所有用来处理请求的东东都变成了终结点。这步是在定义路由时自动完成的。
2.【定义Urls与EndPoints的对应关系】:除了定义终结点我们还要定义 请求路径 与 终结点的对应关系,请求抵达时才能匹配找到合适的终结点来处理我们的请求,这步相当于定义路由。
3.【解析Url->EndPoint】:定义一个解析器,当请求抵达时根据终结点与路径的对应关系找到终结点,微软已定义好对应的中间件来表示这个解析器。
4.【EndPoint->委托】:最后需要定义一个中间件,在上面的中间件执行后,就可以拿到与当前请求匹配的终结点,最终调用它的委托处理请求,这个中间件就是mvc中间件
5.【3-4之间】:到此asp.net core 3.x的中间件路由默认差不多就这样了,此时可以定义自己的中间件,放在步骤3后面,拿到终结点做一些高级处理。微软定义的一些中间件也是这个套路。
参考
https://www.cnblogs.com/artech/p/asp-net-core-routing-01.html
https://www.cnblogs.com/RandyField/p/12770992.html
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步