.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

posted @ 2020-04-28 23:29  feipeng8848  阅读(349)  评论(0编辑  收藏  举报