3. 应用启动

3.1 Startup

Startup 类是 ASP.NET Core 应用程序启动默认调用的类,该类是在 Program.cs 中配置:

Fur.Web.Entry\Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Fur.Web.Entry
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.Inject()
.UseStartup<Startup>();
});
}
}
}

3.1.1 Startup 两个重要方法

Startup 默认有两个重要的方法:

  • ConfigureServices:配置应用所需服务,在该方法中可以添加应用所需要的功能或服务
  • Configure:配置应用请求处理管道

默认代码如下:

Fur.Web.Entry\Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace Fur.Web.Entry
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}
}

在这里,不打算详细讲 Startup 类的具体功能和作用。

了解更多

想了解更多 Startup 知识可查阅 ASP.NET Core - Startup 类 章节。

3.2 AppStartup

Fur 框架中,提供了更为灵活的 Startup 类配置方式,无需在 Web 启用层 中配置,可将配置放到任何项目层。

可能会有读者有疑问,为什么要多此一举呢?原因有几点:

  • Startup 类默认和 Web 应用层 绑定在一起,这样就会导致如果我创建了新的 Web 应用层Startup 又要重新配置
  • 随着业务的增长,需要集成越来越多的第三方服务,这时候 Startup 类就会变得越来越臃肿,难以维护
  • Startup 类无法与其他项目类型进行共用

所以,Fur 提供了更加灵活的配置方式:AppStartup

3.2.1 如何配置 AppStartup

AppStartup 是一个抽象的空类,没有任何定义成员。正是因为这样,才提供更加灵活的配置方式。

3.2.2 AppStartup 约定

AppStartup 派生类只有两个小约定:

  • 任何公开、非静态、返回值为 void 且方法第一个参数是 IServiceCollection 类型,那么他就是一个 ConfigureServices 方法
  • 任何公开、非静态、返回值为 void 且方法第一个参数是 IApplicationBuilder 类型,第二个参数是 IWebHostEnvironment 类型,那么他就是一个 Configure 方法

所以,我们可以自由的编写方法,只要遵循约定即可,如:

using Microsoft.Extensions.DependencyInjection;
namespace Fur.EntityFramework.Core
{
public class MyStartup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDataValidation();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
}
// 可以随意定义名字和方法
public void XXXXName(IServiceCollection services)
{
}
// 可以随意定义名字和方法
public void ZZZName(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}
}

3.2.3 AppStartup 配置顺序

默认情况下,AppStartup 配置顺序由所在程序集的名称进行正序调用,如果我们需要配置执行顺序,只需要在 AppStartup 派生类中贴 [AppStartup(order)] 特性即可。

order 数值越大,越在前面调用,如:

using Microsoft.Extensions.DependencyInjection;
namespace Fur.EntityFramework.Core
{
[AppStartup(10)]
public class FirstStartup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}
}
using Microsoft.Extensions.DependencyInjection;
namespace Fur.EntityFramework.Core
{
[AppStartup(9)]
public class SecondStartup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}
}

FirstStartup 会在 SecondStartup 之前调用。

3.2.4 AppStartup 方法调用顺序

AppStartup 方法调用顺序和方法的书写先后有关,越在前面的方法越先调用。

3.3 框架默认 AppStartup 实现

Fur 框架默认在每一个项目层中都创建了一个 AppStartup 派生类,方便大家配置。如:

  • FurStartup.csFur 框架核心启动类(通常无需配置)
  • FurApplicationStartup.cs:业务服务启动类
  • FurCoreStartup.cs:应用核心功能启动类
  • FurEntityFrameworkCoreStartup.cs:数据库相关启动类
  • FurWebCoreStartup.csWeb 核心启动配置类

3.4 Startup 配置最佳实践

建议 Web 启动层的 Startup.cs 保持为空方法体,如:

Fur.Web.Entry\Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace Fur.Web.Entry
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}
}

将所有 Web 应用层 配置迁移到 FurWebCoreStartup.cs 中,如:

Fur.Web.Core\FurWebCoreStartup.cs
using Fur.UnifyResult;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Fur.Web.Core
{
[AppStartup(800)]
public sealed class FurWebCoreStartup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCorsAccessor();
services.AddSpecificationDocuments();
services.AddControllers()
.AddDynamicApiControllers()
.AddDataValidation()
.AddFriendlyException()
.AddUnifyResult<RESTfulResult, RESTfulResultProvider>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCorsAccessor();
app.UseAuthentication();
app.UseAuthorization();
app.UseSpecificationDocuments();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

这样,后续更换 Web 应用层 也无需重新配置 Startup.cs

3.5 关于 appsettings.json

在默认情况下,ASP.NET Core 配置放在 appsettings.json 中配置,但是这样的方式和 Startup.cs 配置一样的道理,一旦我们更换了 Web 应用层,那么 appsettings.json 又要重新配置一次。

所以,Fur 框架提供了更加灵活的方式配置 appsettings.json只需要在任何项目层根目录下创建 .json 文件即可

Fur 框架最后会自动合并所有分散的配置文件。

如我们在 Fur.EntityFramework.Core 层创建 dbsettings.json 配置数据库连接字符串,如:

Fur.EntityFramework.Core\dbsettings.json
{
"ConnectionStrings": {
"DbConnectionString": "Server=localhost;Database=Fur;User=sa;Password=000000;MultipleActiveResultSets=True;",
"Sqlite3ConnectionString": "Data Source=./Fur.db"
}
}

无需在 appsettings.json 中配置,下面是 appsettings.json 默认代码:

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore": "Information"
}
},
"AllowedHosts": "*"
}

这样我们把配置文件分散在不同项目层之后,就可以实现共用和共享了。

3.6 神奇的 Inject()

Inject() 方法是 Fur 框架提供的最小侵入式的方法,可以让任何 ASP.NET Core Web 项目瞬间支持所有 Fur 框架特性。

我们创建了新的 Web 项目,只需要三个步骤即可:

  • 添加 Fur.Web.Core 项目引用
  • Program.cs 中调用即可
  • Startup.cs 代码迁移到 Fur.Web.Core 项目的 FurWebCoreStartup.cs

如:

Fur.Web.Entry\Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Fur.Web.Entry
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.Inject()
.UseStartup<Startup>();
});
}
}
}
Fur.Web.Entry\Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace Fur.Web.Entry
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}
}
Startup.cs 代码迁移

只需要将 ConfigureServicesConfigure 方法代码迁移到 FurWebCoreStartup.cs 中即可,而 Startup.cs 中两个方法留空即可。

非常简单吧。我们后续创建任何 MVCRazorPagesBlazor 项目只需要添加 Fur.Web.Core 引用和调用 Inject() 即可。

3.7 反馈与建议

与我们交流

给 Fur 提 Issue