前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASP.NET Core 6框架揭秘实例演示[23]:ASP.NET Core应用承载方式的变迁

ASP.NET Core 6框架揭秘实例演示[23]:ASP.NET Core应用承载方式的变迁

作者头像
蒋金楠
发布2022-05-09 12:47:20
9280
发布2022-05-09 12:47:20
举报
文章被收录于专栏:大内老A大内老A

ASP.NET Core应用本质上就是一个由中间件构成的管道,承载系统将应用承载于一个托管进程中运行起来,其核心任务就是将这个管道构建起来。从设计模式的角度来讲,“管道”是构建者(Builder)模式最典型的应用场景,所以ASP.NET Core先后采用的三种承载方式都是采用这种模式。(本篇提供的实例已经汇总到《ASP.NET Core 6框架揭秘-实例演示版》)

[S1501]基于IWebHost/IWebHostBuilder的应用承载方式(源代码) [S1502]将初始化设置定义在Startup类型中(源代码) [S1503]基于IHost/IHostBuilder的应用承载方式(源代码) [S1504]Minimal API(源代码

[S1501]基于IWebHost/IWebHostBuilder的应用承载方式

ASP.NET Core Core 1.X/2.X采用的承载模型以IWebHostBuilder和IWebHost为核心。IWebHost对象代表承载Web应用的宿主(Host),管道随着IWebHost对象的启动被构建出来。IWebHostBuilder对象作为宿主对象的构建者,我们针对管道构建的设置都应用在它上面。

这种“原始”的应用承载方式依然被保留了下来,如下这个Hello World应用就是采用的这种承载方式。如代码片段所示,我们先创建一个实现了IWebHostBuilder接口的WebHostBuilder对象,并调用其UseKestrel扩展方法注册了一个Kestrel服务器。我们接下来调用它的Configure方法利用提供的Action<IApplicationBuilder>委托注册了一个中间件,该中间件将指定的“Hello World”文本作为响应内容。我们调用IWebHostBuilder对象的Build方法将作为宿主的IWebHost对象构建出来后,我们调用其Run扩展方法将它启动起来。此时同构注册的服务器和中间件组成的管道被构建起来,服务器开始监听、接收请求,在将请求交付给后续的中间件进行处理后,它会将响应回复给客户端。

代码语言:javascript
复制
new WebHostBuilder()
    .UseKestrel()
    .Configure(app => app.Run(context => context.Response.WriteAsync("Hello World!")))
    .Build()
.Run();

按照“面向接口编程”的原则,其实我们不应该调用构造函数去创建一个“空”的WebHostBuilder对象并自行完成针对它的所有设置,而是选择按照如下的方式调用定义在静态类型WebHost中的工厂方法CreateDefaultBuilder去创建一个具有默认设置的IWebHostBuilder对象。由于Kestrel服务器的配置就属于“默认设置”的一部分,针对UseKestrel扩展方法的调用也不再需要。

代码语言:javascript
复制
using Microsoft.AspNetCore;

WebHost.CreateDefaultBuilder()
    .Configure(app => app.Run(context => context.Response.WriteAsync("Hello World!")))
    .Build()
.Run();

[S1502]将初始化设置定义在Startup类型中

如果管道涉及过多的 中间件需要注册,我们还可以将“中间件注册”这部分工作实现在一个按照约定定义的Startup类型中。由于ASP.NET Core建立在依赖注入框架之上,所以应用往往需要涉及到很多服务注册,我们一般也会将“服务注册”的工作也放在这个Startup类型中。我们最终只需要按照如下的方式将这个Startup注册到创建的IWebHostBuilder对象上就可以了。

代码语言:javascript
复制
using Microsoft.AspNetCore;

WebHost.CreateDefaultBuilder()
    .UseStartup<Startup>()
    .Build()
    .Run();

public class Startup
{
    public void ConfigureServices(IServiceCollection services)=> services.AddSingleton<IGreeter, Greeter>();
    public void Configure(IApplicationBuilder app, IGreeter greeter)=> app.Run(context => context.Response.WriteAsync(greeter.Greet()));
}

public interface IGreeter
{
    string Greet();
}

public class Greeter : IGreeter
{
    public string Greet() => "Hello World!";
}

[S1503]基于IHost/IHostBuilder的应用承载方式

除了承载Web应用,我们还有很多针对后台服务(比如很多批处理任务)的承载需求,为此微软推出了以IHostBuilder/IHost为核心的服务承载系统。Web应用本身实际上就是一个长时间运行的后台服务,我们完全可以将应用定义成一个IHostedService服务(GenericWebHostService)。如果将上面介绍的称为第一代应用承载模式的话,这就是第二代承载模式。IHostBuilder接口定义的很多方法(其中很多是扩展方法)旨在完成两个方面的设置:第一,为创建的IHost对象及承载的IHostedService服务注册依赖服务;第二,为服务承载和应用提供相应的配置。如果采用基于IWebHostBuilder/IWebHost的承载方式,上述这两方面的设置由IWebHostBuilder对象来完成,后者在此基础上还提供了针对中间件的注册。

虽然IWebHostBuilder接口提供的除中间件注册的其他设置基本可以调用IHostBuilder接口相应的方法来完成,但是由于IWebHostBuilder承载的很多配置都是以扩展方法的形式提供的,所以有必要提供针对IWebHostBuilder接口的兼容。基于IHostBuilder/IHost的承载系统中提供对IWebHostBuilder接口的兼容是通过如下所示的ConfigureWebHost扩展方法达成的,GenericWebHostService承载服务也是在这个方法中被注册的。ConfigureWebHostDefaults扩展方法则会在此基础上做一些默认设置(如KestrelServer)。

代码语言:javascript
复制
public static class GenericHostWebHostBuilderExtensions
{
    public static IHostBuilder ConfigureWebHost(this IHostBuilder builder,Action<IWebHostBuilder> configure);
    public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure, Action<WebHostBuilderOptions> configureWebHostBuilder);
    public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
}

如果采用基于IHostBuilder/IHost的承载方式,上面演示的“Hello World”应用可以改写成如下的形式。如代码片段所示,在调用Host的静态工厂方法CreateDefaultBuilder创建出具有默认设置的IHostBuilder对象之后,我们调用它的ConfigureWebHostDefaults扩展方法针对承载ASP.NET Core应用的GenericWebHostService做进一步设置。该方法提供的Action<IApplicationBuilder>委托完成了针对Startup类型的注册(S1503)。

代码语言:javascript
复制
Host.CreateDefaultBuilder()
    .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.UseStartup<Startup>())
    .Build()
    .Run();

public class Startup
{
    public void ConfigureServices(IServiceCollection services) => services.AddSingleton<IGreeter, Greeter>();
    public void Configure(IApplicationBuilder app, IGreeter greeter)=> app.Run(context => context.Response.WriteAsync(greeter.Greet()));
}

public interface IGreeter
{
    string Greet();
}

public class Greeter : IGreeter
{
    public string Greet() => "Hello World!";
}

[S1504]Minimal API

“天下大势,分久必合,合久必分”,ASP.NET Core应用通过GenericWebHostService这个承载服务被整合到基于IHostBuilder/IHost的服务承载系统中之后,也许微软还是意识到Web应用和后台服务的承载方式还是应该加以区分,而且它们采用的SDK都不一样(ASP.NET Core应用采用的SDK为“Microsoft.NET.Sdk.Web”,后台服务采用的SDK一般为“Microsoft.NET.Sdk.Worker”),于是推出了基于WebApplicationBuilder/WebApplication的承载方式。但这一次并非又回到了起点,因为底层的承载方式其实没有改变,它只是在上面再封装了一层而已。

新的应用承载方式依然采用“构建者(Builder)”模式,核心的两个对象分别为WebApplication和WebApplicationBuilder,代表承载应用的WebApplication对象由WebApplicationBuilder对象进行构建。我们可以将其称为第三代承载模式,它有一个官方的名称叫做“Minimal API”。第二代承载模式需要提供针对IWebHostBuilder接口的兼容,作为第三代承载模式的Minimal API则需要同时提供针对IWebHostBuilder和IHostBuilder接口的兼容,此兼容性是通过这两个接口的实现类型ConfigureWebHostBuilder和ConfigureHostBuilder达成的。

WebApplicationBuilder类型的WebHost和Host属性返回了这两个对象,之前定义在IWebHostBuilder和IHostBuilder接口上的绝大部分API(并非所有API)借助它们得以复用。也正是有了这段历史,我们会发现相同的功能具有两到三种不同的编程方式。比如IWebHostBuilder和IHostBuilder接口上都提供了注册服务的方法,而WebApplicationBuilder类型利用Services属性直接将存放服务注册的IServiceCollection对象暴露出来,所以任何的服务注册都可以利用这个属性来完成。

代码语言:javascript
复制
public sealed class WebApplicationBuilder
{
    public ConfigureWebHostBuilder 	WebHost { get; }
    public ConfigureHostBuilder 	Host { get; }

    public IServiceCollection 	        Services { get; }
    public ConfigurationManager 	Configuration { get; }
    public ILoggingBuilder 		Logging { get; }

    public IWebHostEnvironment 	        Environment { get; }

    public WebApplication Build();
}

public sealed class ConfigureWebHostBuilder : IWebHostBuilder, ISupportsStartup
public sealed class ConfigureHostBuilder : IHostBuilder, ISupportsConfigureWebHost

IWebHostBuilder和IHostBuilder接口都提供了设置配置和日志的方法,这两方面的设置都可以利用WebApplicationBuilder利用Configuration和Logging暴露出来的ConfigurationManager和ILoggingBuilder对象来实现。既然我们采用了Minimal API,那么我们就应该尽可能得使用WebApplicationBuilder类型提供得API。如果采用这种全新的承载方式,我们演示的Hello World程序可以改写成如下的形式。如代码片段所示,我们调用定义在WebApplication类型中的静态工厂方法CreateBuilder根据指定的命令行参数(args)创建一个WebApplicationBuilder对象,并调用其Build方法构建出对代表承载Web应用的WebApplication对象。

代码语言:javascript
复制
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(context => context.Response.WriteAsync("Hello World!"));
app.Run();

接下来我们调用了它的两个Run方法,调用得第一个Run方法是IApplicationBuilder接口(WebApplication类型实现了该接口)的扩展方法是为了注册中间件,调用第二个Run方法才是启动WebApplication对象代表的应用。由于我们并没有在WebApplicationBuilder对象上作任何设置,所以我们可以按照如下的方式调用WebApplication的静态Create方法将WebApplication对象创建出来。

代码语言:javascript
复制
var app = WebApplication.Create(args);
app.Run(context => context.Response.WriteAsync("Hello World!"));
app.Run();

值得一提的是,之前的两种承载方式都倾向于将初始化操作定义在注册的Startup类型中,这种编程在Minimal API中不再被支持,所以如下的程序虽然可以成功编译,但是执行的时候会抛出异常。

代码语言:javascript
复制
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseStartup<Startup>();
var app = builder.Build();
app.Run();
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-03-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • [S1501]基于IWebHost/IWebHostBuilder的应用承载方式
  • [S1502]将初始化设置定义在Startup类型中
  • [S1503]基于IHost/IHostBuilder的应用承载方式
  • [S1504]Minimal API
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档