ASP.NET Core 6框架揭秘实例演示[23]:ASP.NET Core应用承载方式的变迁
ASP.NET Core应用本质上就是一个由中间件构成的管道,承载系统将应用承载于一个托管进程中运行起来,其核心任务就是将这个管道构建起来。从设计模式的角度来讲,“管道”是构建者(Builder)模式最典型的应用场景,所以ASP.NET Core先后采用的三种承载方式都是采用这种模式。(本篇提供的实例已经汇总到《ASP.NET Core 6框架揭秘-实例演示版》)
[S1501]基于IWebHost/IWebHostBuilder的应用承载方式(源代码)
[S1502]将初始化设置定义在Startup类型中(源代码)
[S1503]基于IHost/IHostBuilder的应用承载方式(源代码)
[S1504]将初始化设置定义在Startup类型中(源代码)
[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扩展方法将它启动起来。此时同构注册的服务器和中间件组成的管道被构建起来,服务器开始监听、接收请求,在将请求交付给后续的中间件进行处理后,它会将响应回复给客户端。
new WebHostBuilder()
.UseKestrel()
.Configure(app => app.Run(context => context.Response.WriteAsync("Hello World!")))
.Build()
.Run();
按照“面向接口编程”的原则,其实我们不应该调用构造函数去创建一个“空”的WebHostBuilder对象并自行完成针对它的所有设置,而是选择按照如下的方式调用定义在静态类型WebHost中的工厂方法CreateDefaultBuilder去创建一个具有默认设置的IWebHostBuilder对象。由于Kestrel服务器的配置就属于“默认设置”的一部分,针对UseKestrel扩展方法的调用也不再需要。
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对象上就可以了。
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)。
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)。
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]将初始化设置定义在Startup类型中
“天下大势,分久必合,合久必分”,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对象暴露出来,所以任何的服务注册都可以利用这个属性来完成。
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对象。
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对象创建出来。
var app = WebApplication.Create(args);
app.Run(context => context.Response.WriteAsync("Hello World!"));
app.Run();
值得一提的是,之前的两种承载方式都倾向于将初始化操作定义在注册的Startup类型中,这种编程在Mininal API中不再被支持,所以如下的程序虽然可以成功编译,但是执行的时候会抛出异常。
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseStartup<Startup>();
var app = builder.Build();
app.Run();
ASP.NET Core 6框架揭秘实例演示[23]:ASP.NET Core应用承载方式的变迁的更多相关文章
- ASP.NET Core 6框架揭秘实例演示[24]:中间件的多种定义方式
ASP.NET Core的请求处理管道由一个服务器和一组中间件组成,位于 "龙头" 的服务器负责请求的监听.接收.分发和最终的响应,针对请求的处理由后续的中间件来完成.中间件最终体 ...
- ASP.NET Core 6框架揭秘实例演示[21]:如何承载你的后台服务
借助 .NET提供的服务承载(Hosting)系统,我们可以将一个或者多个长时间运行的后台服务寄宿或者承载我们创建的应用中.任何需要在后台长时间运行的操作都可以定义成标准化的服务并利用该系统来承载,A ...
- ASP.NET Core 6框架揭秘实例演示[07]:文件系统
ASP.NET Core应用具有很多读取文件的场景,如读取配置文件.静态Web资源文件(如CSS.JavaScript和图片文件等).MVC应用的视图文件,以及直接编译到程序集中的内嵌资源文件.这些文 ...
- ASP.NET Core 6框架揭秘实例演示[08]:配置的基本编程模式
.NET的配置支持多样化的数据源,我们可以采用内存的变量.环境变量.命令行参数.以及各种格式的配置文件作为配置的数据来源.在对配置系统进行系统介绍之前,我们通过几个简单的实例演示一下如何将具有不同来源 ...
- ASP.NET Core 6框架揭秘实例演示[09]:配置绑定
我们倾向于将IConfiguration对象转换成一个具体的对象,以面向对象的方式来使用配置,我们将这个转换过程称为配置绑定.除了将配置树叶子节点配置节的绑定为某种标量对象外,我们还可以直接将一个配置 ...
- ASP.NET Core 6框架揭秘实例演示[10]:Options基本编程模式
依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中.除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对 ...
- ASP.NET Core 6框架揭秘实例演示[11]:诊断跟踪的几种基本编程方式
在整个软件开发维护生命周期内,最难的不是如何将软件系统开发出来,而是在系统上线之后及时解决遇到的问题.一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根 ...
- ASP.NET Core 6框架揭秘实例演示[12]:诊断跟踪的进阶用法
一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根据当前的运行状态预知未来可能发生的问题,并将问题扼杀在摇篮中.诊断跟踪能够帮助我们有效地纠错和排错&l ...
- ASP.NET Core 6框架揭秘实例演示[13]:日志的基本编程模式[上篇]
<诊断跟踪的几种基本编程方式>介绍了四种常用的诊断日志框架.其实除了微软提供的这些日志框架,还有很多第三方日志框架可供我们选择,比如Log4Net.NLog和Serilog 等.虽然这些框 ...
随机推荐
- tcp协议下的Socket
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net ...
- 关于setInterval方法中function的定义方法
使用window对象的setInterval方法,作为第一个参数传递的function必须在全局作用域中定义,否则会出现报错而无法执行. 具体如下: 在下面的代码中,试用jQuery方式在回调函数中使 ...
- LVS调度算法总结
LVS 调试算法分为两种:静态方法和动态方法. 静态方法 RR:轮询 WRR:加权轮询 SH:源地址哈希,将来自于同一个IP地址的请求始终发往第一次挑中的RS,从而实现会话绑定 DH:目标地址哈希,第 ...
- python基础语法_字符串编码
Python常用字符编码 http://www.cnblogs.com/schut/p/8406897.html Python常见字符编码间的转换 在字符串写入文件时,有时会因编码问题导致无法 ...
- 国外很便宜的服务器 一年 2核2G 一年20美元
今年 服务器 //=====================================一下是去年的================================= 优惠码:zhujicepin ...
- 联邦学习:按Dirichlet分布划分Non-IID样本
我们在<Python中的随机采样和概率分布(二)>介绍了如何用Python现有的库对一个概率分布进行采样,其中的dirichlet分布大家一定不会感到陌生.该分布的概率密度函数为 \[P( ...
- 十年OI一场空,不开long long见祖宗
//线段树:单点修改+区间求和 #include<bits/stdc++.h> #define ll unsigned long long using namespace std; ll ...
- Linux基础:初识shell之系统命令基础
一 shell介绍 shell Shell 中文意思贝壳,寓意类似内核的壳.Shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务,简而言之就是只要能够操作应用 ...
- Filter(过滤器)与Listener(监听器)详解
11.Filter(重点) Filter:过滤器,用来过滤网站的数据: 处理中文乱码 登陆验证... Filter开发步骤: 导包 编写过滤器 导包不要错 实现Filter接口,重写对应的方法即可 p ...
- 轩辕展览-VR虚拟展厅设计如何实现全景漫游功能
什么是在线3d漫游?如何在VR虚拟展厅设计之中实现3d漫游功能?让我们来分享3dVR虚拟展厅的在线漫游. 实际上,在线3d漫游就是通过3d仿真场景,使用鼠标和键盘在虚拟空间之中自由漫游,它可以从高空俯 ...