[探索 .NET 6]02 比较 WebApplicationBuilder 和 Host

这是『探索 .NET 6』系列的第二篇文章:

  • 01 揭开 ConfigurationManager 的面纱

  • 02 比较 WebApplicationBuilderHost

在 .NET 中,有一种新的“默认”方法用来构建应用程序,即使用 WebApplication.CreateBuilder()。在这篇文章中,我将这种方法与以前的方法进行了比较,讨论了为什么要进行这种改变,并看看其影响。在下一篇文章中,我将看一下 WebApplicationWebApplicationBuilder 背后的代码,看看它们是如何工作的。

1构建 ASP.NET Core 应用:一个历史教训

在我们看 .NET 6 之前,我认为值得看看 ASP.NET Core 应用程序的“启动”过程在过去几年中是如何演变的,因为最初的设计对我们今天的情况有很大的影响。当我们在下一篇文章中查看 WebApplicationBuilder 背后的代码时,这一点将变得更加明显!

即使我们忽略了 .NET Core 1.x(目前完全不支持),我们也有三种不同的范式来配置 ASP.NET Core 应用程序。

  • WebHost.CreateDefaultBuilder():配置 ASP.NET Core 应用程序的“原始”方法,截至 ASP.NET Core 2.x。

  • Host.CreateDefaultBuilder():在通用 Host 的基础上重新构建 ASP.NET Core,支持其他如 Worker 服务的工作负载。.NET Core 3.x 和 .NET 5 中的默认方法。

  • WebApplication.CreateBuilder():.NET 6 中的新热点。

为了更好地了解这些差异,我在下面几节中重现了典型的“启动”代码,这应该会使 .NET 6 的变化更加明显。

2ASP.NET Core 2.x:WebHost.CreateDefaultBuilder()

在 ASP.NET Core 1.x 的第一个版本中,(如果我记得没错的话)没有“默认” Host 的概念。ASP.NET Core 的理念之一是一切都应该“按需付费”,也就是说,如果你不需要使用它,你就不应该为该功能的存在消费资源。

在实践中,这意味着“入门”模板包含了大量的模板,以及大量的 NuGet 包。为了不看到所有这些代码就能开始的快速开发,ASP.NET Core 引入了 WebHost.CreateDefaultBuilder()。这为你设置了一大堆的默认值,创建了一个 IWebHostBuilder,并建立了一个 IWebHost

从一开始,ASP.NET Core 就将 Host 启动与应用程序启动分开。从历史上看,这表现为将你的启动代码分成两个文件,传统上称为 Program.csStartup.cs

9a987150893b06f9b56801f9d7ca194d.png

在 ASP.NET Core 2.1 中,Program.cs 调用 WebHost.CreateDefaultBuilder(),设置你的应用程序配置(例如从 appsettings.json 加载)、日志,以及配置 Kestrel 或 IIS 集成。

public class Program
{public static void Main(string[] args){BuildWebHost(args).Run();}public static IWebHost BuildWebHost(string[] args) =>WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build();
}

默认模板还引用了一个 Startup 类。这个类并没有明确地实现一个接口。相反,IWebHostBuilder 的实现知道寻找 ConfigureServices()Configure() 方法来分别设置你的依赖注入容器和中间件管道。

public class Startup
{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }public void ConfigureServices(IServiceCollection services){services.AddMvc();}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseStaticFiles();app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");});}
}

在上面的启动类中,我们将 MVC 服务添加到容器中,添加了异常处理和静态文件中间件,然后添加了 MVC 中间件。MVC 中间件是最初构建应用程序的唯一真正实用的方法,它同时满足了服务器渲染的视图和 RESTful API 端点。

3ASP.NET Core 3.x/5:HostBuilder

ASP.NET Core 3.x 给 ASP.NET Core 的启动代码带来了一些重大变化。以前,ASP.NET Core 只能真正用于 Web/HTTP 工作负载,但在 .NET Core 3.x 中,做出了支持其他方法的举措:长期运行的“worker services”(例如,用于消费消息队列)、gRPC 服务、Windows 服务等等。我们的目标是与这些其他类型的应用分享专门为构建 Web 应用(配置、日志、DI)而建立的基础框架。

结果是创建了一个“通用 Host”(相对于 Web Host 而言),并在此基础上对 ASP.NET Core 技术栈进行了“重新平台化”。用 IWebHostBuilder 代替了 IHostBuilder

这一变化引起了一些不可避免的破坏性变化,但 ASP.NET 团队尽力为所有针对 IWebHostBuilder 而不是 IHostBuilder 编写的代码提供了指引。其中一个变通方法是 Program.cs 模板中默认使用的 ConfigureWebHostDefaults() 方法:

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>();};}
}

需要 ConfigureWebHostDefaults 来注册 ASP.NET Core 应用程序的 Startup 类,这是 .NET 团队在提供从 IWebHostBuilderIHostBuilder 的迁移路径时面临的挑战之一。Startup 与 Web 应用密不可分,因为 Configure() 方法是配置中间件的。但 worker service 和许多其他应用程序没有中间件,所以 Startup 类作为一个“通用 Host”级别的概念是没有意义的。

这就是 IHostBuilder 上的 ConfigureWebHostDefaults() 扩展方法的作用。这个方法将 IHostBuilder 包裹在一个内部类中,即 GenericWebHostBuilder,并设置 WebHost.CreateDefaultBuilder() 在 ASP.NET Core 2.1 中的所有默认值。GenericWebHostBuilder 作为旧的 IWebHostBuilder 和新的 IHostBuilder 之间的一个适配器。

ASP.NET Core 3.x 的另一个重大变化是引入了端点路由。端点路由是首次尝试使以前仅限于 ASP.NET Core 的 MVC 部分的路由概念可以通用。这需要对你的中间件管道进行一些重新思考,但在许多情况下,必要的改变是最小的。

尽管有这些变化,ASP.NET Core 3.x 中的 Startup 类看起来与 2.x 版本相当相似。下面的例子几乎等同于 2.x 版本(尽管我换成了 Razor Pages 而不是 MVC)。

public class Startup
{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }public void ConfigureServices(IServiceCollection services){services.AddRazorPages();}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseStaticFiles();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapRazorPages();});}
}

ASP.NET Core 5 给现有的应用程序带来的变化相对较少,因此,从 3.x 升级到 5 通常只是简单地改变目标框架和更新一些 NuGet 软件包 🎉。

对于 .NET 6 来说,如果你要升级现有的应用程序,也是这样。但是对于新的应用程序来说,默认的启动体验已经完全改变了...

4ASP.NET Core 6:WebApplicationBuilder

所有以前的 ASP.NET Core 版本都将配置分成两个文件。在 .NET 6 中,C#、BCL 和 ASP.NET Core 的一系列变化意味着现在所有东西都可以放在一个文件中。

请注意,没有人强迫你使用这种风格。我在 ASP.NET Core 3.x/5 代码中展示的所有代码在 .NET 6 中仍然有效。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();var app = builder.Build();if (app.Environment.IsDevelopment())
{app.UseDeveloperExceptionPage();
}app.UseStaticFiles();app.MapGet("/", () => "Hello World!");
app.MapRazorPages();app.Run();

这里有很多变化,但其中最明显的是:

  • 顶层语句意味着没有 Program.Main() 的模板。

  • 隐式 using 指令意味着不需要 using 语句。

  • 没有 Startup 类--所有东西都在一个文件中。

这显然减少了很多代码,但这有必要吗?它又是如何工作的呢?

5所有的代码都去哪儿了

.NET 6 的一大重点是“新人”的视角。作为 ASP.NET Core 的初学者,有一大堆的概念需要你快速理解。只要看看我的书的目录就知道了;有很多东西需要你去理解!

.NET 6 的变化主要集中在消除与入门相关的“仪式”,以及隐藏那些可能让新人感到困惑的概念。比如说:

  • using 语句在入门时是不必要的。尽管工具化通常使这些在实践中成为一个非问题,但当你开始学习时,它们显然是一个不必要的概念。

  • 与此类似,namespace 在你入门时也是一个不必要的概念。

  • Program.Main()...为什么叫这个名字?为什么我需要它?因为你需要。只是现在你不需要了。

  • 配置没有被分割在两个文件中,Program.csStartup.cs。虽然我喜欢这种“关注点分离”,但这要向新来者解释为什么这种分割方式。

  • 当我们谈论 Startup 时,我们不再需要解释“魔术”方法,这些方法可以被调用,尽管它们没有明确地实现一个接口。

此外,我们还有新的 WebApplicationWebApplicationBuilder 类型。这些类型对于实现上述目标并不是严格必要的,但它们确实在某种程度上使配置体验更加干净。

6我们真的需要一个新的类型吗

嗯,不,我们不需要。我们可以用通用 Host 来编写一个与上面的例子非常相似的 .NET 6 应用程序:

var hostBuilder = Host.CreateDefaultBuilder(args).ConfigureServices(services =>{services.AddRazorPages();}).ConfigureWebHostDefaults(webBuilder =>{webBuilder.Configure((ctx, app) =>{if (ctx.HostingEnvironment.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseStaticFiles();app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapGet("/", () => "Hello World!");endpoints.MapRazorPages();});});});hostBuilder.Build().Run();

我想你肯定认同,这看起来比 .NET 6 的 WebApplication 版本要复杂得多。我们有一大堆嵌套的 lambda,它将一个(大部分)程序性的启动脚本变成了更复杂的东西。

WebApplicationBuilder 的另一个好处是,启动时的异步代码要简单得多。你可以在你喜欢的时候调用异步方法。

关于 WebApplicationBuilderWebApplication 的巧妙之处在于,它们基本上等同于上述的通用 Host 的设置,但它们用了一个更简单的 API 来实现。

7大多数配置在 WebApplicationBuilder 中

让我们先来看看 WebApplicationBuilder

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();

WebApplicationBuilder 主要负责 4 项工作:

  • 使用 builder.Configuration 添加配置。

  • 使用 builder.Services 添加服务

  • 使用 builder.Logging 配置日志

  • 配置 IHostBuilderIWebHostBuilder

依次来看...

WebApplicationBuilder 暴露了 ConfigurationManager 类型,用于添加新的配置源,以及访问配置值,正如我在之前的文章中所描述的。

它还直接暴露了一个 IServiceCollection,用于向 DI 容器添加服务。因此,在通用 Host 中,你必须做的是:

var hostBuilder = Host.CreateDefaultBuilder(args);
hostBuilder.ConfigureServices(services =>{services.AddRazorPages();services.AddSingleton<MyThingy>();})

使用 WebApplicationBuilder 你可以:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddSingleton<MyThingy>();

类似的,对于日志,把:

var hostBuilder = Host.CreateDefaultBuilder(args);
hostBuilder.ConfigureLogging(builder =>{builder.AddFile();})

替换成:

var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddFile();

这有完全相同的行为,只是在一个更容易使用的 API 中。对于那些直接依赖 IHostBuilderIWebHostBuilder 的扩展点,WebApplicationBuilder 分别暴露了 HostWebHost 属性。

例如,Serilog 的 ASP.NET Core 集成了 IHostBuilder 勾子。在 ASP.NET Core 3.x/5 中,你用以下方式添加它:

public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).UseSerilog() // <-- Add this line.ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>();});

对于 WebApplicationBuilder,你可以在 Host 属性上调用 UseSerilog()

builder.Host.UseSerilog();

事实上,WebApplicationBuilder 是你做所有配置的地方,除了中间件管道。

8WebApplication 实现了多种接口 

一旦你在 WebApplicationBuilder 上配置了你需要的一切,你就可以调用 Build() 来创建一个 WebApplication 的实例:

var app = builder.Build();

WebApplication 很有趣,因为它实现了多个不同的接口:

  • IHost - 用来启动和停止 Host

  • IApplicationBuilder - 用于建立中间件管道

  • IEndpointRouteBuilder - 用于添加路由端点

后面这两点是非常相关的。在 ASP.NET Core 3.x 和 5 中,IEndpointRouteBuilder 用于通过调用 UseEndpoints() 并向其传递一个 lambda 来添加端点,例如:

public void Configure(IApplicationBuilder app)
{app.UseStaticFiles();app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapRazorPages();});
}

对于刚接触 ASP.NET Core 的人来说,这种 .NET 3.x/5 模式有一些复杂:

  • 中间件管道的构建发生在 StartupConfigure() 函数中(你必须知道去看那里)。

  • 你必须确保在 app.UseEndpoints() 之前调用 app.UseRouting()(以及将其他中间件放在正确的位置)。

  • 你必须使用 lambda 来配置端点(对于熟悉 C# 的用户来说并不复杂,但对于新人来说可能会感到困惑)。

WebApplication 大大简化了这种模式:

app.UseStaticFiles();
app.MapRazorPages();

这显然要简单得多,尽管我发现它有点令人困惑,因为中间件和端点之间的区别远没有 .NET 5.x 等中那么清晰。这可能只是个人看法不同,但我认为这混淆了“顺序很重要”的信息(这适用于中间件,但一般不适用端点)。

我还没有展示的是 WebApplicationWebApplicationBuilder 是如何构建的。在下一篇文章中,我将揭开幕布,让我们看到幕后的真实情况。

9总结

在这篇文章中,我描述了 ASP.NET Core 应用程序的启动从 2.x 版本一直到 .NET 6 的变化。我展示了 .NET 6 中引入的新的 WebApplicationWebApplicationBuilder 类型,讨论了它们被引入的原因,以及它们带来的一些优势。最后,我讨论了这两个类所扮演的不同角色,以及它们的 API 如何使启动体验更简单。在下一篇文章中,我将看一下这些类型背后的一些代码,看看它们是如何工作的。

原文:bit.ly/3fDZlS9
作者:Andrew Lock
翻译:精致码农

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/292316.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

都怪爱因斯坦没说清楚!竟有人相信一个粉笔头就能让全人类多喝100年的热水?...

全世界只有3.14 % 的人关注了爆炸吧知识一个粉笔头一共能释放多少能量爱因斯坦大家肯定都熟悉&#xff0c;相信也有很多朋友听说过质能方程。根据质能方程的公式&#xff0c;我们发现&#xff1a;似乎能量和质量是可以相互转化的。尤其是一些没有系统学习过相对论&#xff0c;又…

从微信云托管容器镜像的选择-alpine 说起

微信云托管 使用目前主流的容器平台Docker以及容器编排技术Kubernetes&#xff08;简称K8S&#xff09;&#xff0c;来管理你的项目。使用微信云托管需要掌握对Docker的使用&#xff0c;但你无需掌握K8S的使用方法。微信云托管将K8S的运维配置完全接手&#xff0c;你不需要关心…

H5移动开发AUI框架入门---博客园老牛大讲堂

大家都知道H5可以开发移动端的页面&#xff0c;网上提供的移动端的开发都有很多。因为我学习了AUI框架&#xff0c;所以我这里介绍一下移动端AUI框架。--博客园老牛大讲堂 一、AUI框架是什么&#xff1f;---博客园老牛大讲堂 AUI框架就是利用原生的js和css封装成的一些界面。当…

.NET6使用DOCFX根据注释自动生成开发文档

本文内容来自我写的开源电子书《WoW C#》&#xff0c;现在正在编写中&#xff0c;可以去WOW-Csharp/学习路径总结.md at master sogeisetsu/WOW-Csharp (github.com)来查看编写进度。预计2021年年底会完成编写&#xff0c;2022年2月之前会完成所有的校对和转制电子书工作&…

量子力学到底神奇在哪里?看完这个,我的认知彻底坍塌了

▲ 点击查看很多朋友应该都看过Facebook创始人扎克伯格给他的女儿讲量子力学的那张照片。扎克伯格在清华大学经济管理学院做演讲时&#xff0c;曾谈到&#xff1a;学习量子力学改变了他的思维方式。到底什么是量子力学&#xff1f;我们生活面对的物质尺度大约是厘米级到千米级之…

linux 路由表设置 之 route 指令详解

使用下面的 route 命令可以查看 Linux 内核路由表。 [cpp] view plaincopy# route Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.0.0 * 255.255.255.0 U 0 0 0 eth0 169.254.0.0 * …

黄老师离开呆了十年的上海

关注我的老朋友都知道&#xff0c;我和一线码农&#xff08;黄新成&#xff09;以前是同事&#xff0c;我以前也写过文章说过他的事迹。我们曾经一起共事过一家电商服务公司&#xff0c;每天和千万量级的数据打交道。.NET 圈的朋友想必很多人都看过一线码农写的技术文章&#x…

Shell配置_配置IP

1、setup 打开图形化页面a) 选择网络配置b) 选择设置配置c) 选择第一个网卡2、启动网卡&#xff08;第一个网卡&#xff09;vim /etc/sysconfig/network-scripts/ifcfg-eth0将ONBOOT"no"改为ONBOOT"yes"3、重启网络服务service network restart来自为知笔记…

linux之路由知识之ip route 命令中的疑惑

1.基础知识 1.1 路由 &#xff08;Routing&#xff09; 1.1.1 路由策略 &#xff08;使用 ip rule 命令操作路由策略数据库&#xff09; 基于策略的路由比传统路由在功能上更强大&#xff0c;使用更灵活&#xff0c;它使网络管理员不仅能够根据目的地址而且能够根据报文大小、应…

python怎么执行程序_小鹅通视频怎么下载?用python实现小鹅通视频下载(二)

小鹅通视频怎么下载&#xff1f;用python实现小鹅通视频下载(二)背景上次分享《小鹅通视频怎么下载&#xff1f;用python实现小鹅通视频下载(一)[1]》后&#xff0c;引来了很多人咨询小鹅通视频怎么下载的问题。其实咨询的人大多是不懂python[2]语言的人&#xff0c;也有一部分…

在 Azure Functions 上使用不同的路由前缀

点击上方蓝字关注“汪宇杰博客”原文&#xff1a;Azure Tips and Tricks翻译&#xff1a;汪宇杰导语有时需要使用与 Azure Functions 自动生成的路由前缀不同的路由前缀。例如&#xff1a;https://mynewapimc.azurewebsites.net/api/HttpTriggerCSharp1 在函数名之前使用 api。…

RabbitMq、ActiveMq、ZeroMq、kafka之间的比较,资料汇总

2019独角兽企业重金招聘Python工程师标准>>> MQ框架非常之多&#xff0c;比较流行的有RabbitMq、ActiveMq、ZeroMq、kafka。这几种MQ到底应该选择哪个&#xff1f;要根据自己项目的业务场景和需求。下面我列出这些MQ之间的对比数据和资料。 第一部分&#xff1a;Rab…

TMG学习(十一),保护企业内网上网安全

我们知道一个企业如果允许用户上网而且可以任意下载软件&#xff0c;这对于企业来说是非常危险的&#xff0c;用户无法辨别哪些是安全网站哪些网站存在风险&#xff0c;因此一旦用户在恶意网站上下载了软件&#xff0c;导致客户端中毒从而会影响整个企业内部网络&#xff0c;最…

sql跨表查询_跨表更新,看到自己写的SQL像个憨憨

有点 SQL 基础的朋友肯定听过 「跨表查询」&#xff0c;那啥是跨表更新啊&#xff1f;背景项目新导入了一批人员数据&#xff0c;这些人的有的部门名称发生了变化&#xff0c;有的联系方式发生了变化&#xff0c;暂且称该表为t_dept_members, 系统中有另外一张表 t_user_info 记…

iText创建一个含有中文的pdf文档

有朋友问我pdfbox支不支持向pdf文档中写入中文。然后试了好多遍都是有乱码&#xff0c;也找了好多资料没有找到解决办法。 但是在查找资料的过程中发现了另一个处理pdf的开源库iText.官方介绍 http://itextpdf.com/ 在这参考了两篇博客 博客1.pdfbox&iText生成PDF文件格式及…

万字长文 - 解读功能开关 | IDCF

原文&#xff1a;https://martinfowler.com/articles/feature-toggles.html作者&#xff1a;Pete Hodgson译者&#xff1a;冬哥功能开关Feature Toggle&#xff08;通常也称为功能标志Feature Flag&#xff09;是一种强大的技术&#xff0c;允许团队在不更改代码的情况下修改系…