Host 層
目前模組基礎架構已完成。為了提升擴展性並確保未來能直接轉成微服務,我們需建立一個專屬的 API Host 專案。
這個 API Host 專案扮演的是模組使用者的角色,所以並不要在這裡寫商業邏輯,應該把它當作指揮中心負責組裝與調動需要的模組, 另外還需要提供啟動 Web 的工作環境。
WebApplication 啟動
由於模組中的 Console 與 DbMigrator 專案採用的是 .NET Generic Host 模式,
只適合用在 Console 或 Worker 類型專案上,但缺乏 Web 環境所需的 HTTP 管道、Middleware 及路由等基礎設施。
因此,API Host 專案要改用 WebApplication 模式啟動。
這部分跟標準的 .NET Web 使用方式一致,透過 WebApplication.CreateBuilder(args) 初始化 Builder 環境,
接下來調用 ABP 的擴充方法 AddApplicationAsync 掃描模組依賴關係並根據順序註冊所有依賴注入。
await builder.AddApplicationAsync<MyProjectHostModule>();
此步驟會啟動模組掃描機制,ABP 會根據 DependsOn 計算模組依賴。接著,
它會按正確的順序執行各個模組中的 ConfigureServices 方法,自動完成所有 DI 註冊。
當執行 builder.Build() 取得 WebApplication 實例後,調用:
await app.InitializeApplicationAsync();
這個是 ABP 的關鍵。傳統 ASP.NET Core 需要在 Program.cs 手動排定 app.Use... 的順序,
但在 ABP 會依據模組間的依賴關係,自動決定 Middleware 的掛載順序,並且將原本堆積在 Program.cs 的 Middleware 配置,
分散到各個模組的 OnApplicationInitialization 生命週期方法中,這樣使用者就不需要關心模組的啟動順序,實現即插即用。
Host 實做
接下來新增 BookStoreScratch.HttpApi.Host 專案
mkdir host
dotnet new web -o host/BookStoreScratch.HttpApi.Host
dotnet sln add host/BookStoreScratch.HttpApi.Host
dotnet add host/BookStoreScratch.HttpApi.Host package Volo.Abp.Autofac --version 9.0.2
dotnet add host/BookStoreScratch.HttpApi.Host package Volo.Abp.Swashbuckle --version 9.0.2
dotnet add host/BookStoreScratch.HttpApi.Host package Volo.Abp.EntityFrameworkCore.PostgreSql --version 9.0.2
dotnet add host/BookStoreScratch.HttpApi.Host package Serilog.AspNetCore --version 8.0.2
dotnet add host/BookStoreScratch.HttpApi.Host package Serilog.Sinks.Async --version 2.1.0
dotnet add host/BookStoreScratch.HttpApi.Host package Serilog.Sinks.Console --version 6.1.1
dotnet add host/BookStoreScratch.HttpApi.Host package Serilog.Extensions.Logging --version 9.0.2
dotnet add host/BookStoreScratch.HttpApi.Host/BookStoreScratch.HttpApi.Host.csproj reference src/BookStoreScratch.Application/BookStoreScratch.Application.csproj
dotnet add host/BookStoreScratch.HttpApi.Host/BookStoreScratch.HttpApi.Host.csproj reference src/BookStoreScratch.EntityFrameworkCore/BookStoreScratch.EntityFrameworkCore.csproj
dotnet add host/BookStoreScratch.HttpApi.Host/BookStoreScratch.HttpApi.Host.csproj reference src/BookStoreScratch.HttpApi/BookStoreScratch.HttpApi.csproj
建立 BookStoreScratchHttpApiHostModule 模組,這裡註冊 Swagger 與 PostgreSQL 與設定必要的 Middleware。
using BookStoreScratch.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using Volo.Abp;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
namespace BookStoreScratch.HttpApi.Host;
[DependsOn(
typeof(BookStoreScratchApplicationModule),
typeof(BookStoreScratchHttpApiModule),
typeof(BookStoreScratchEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCorePostgreSqlModule),
typeof(AbpAutofacModule),
typeof(AbpSwashbuckleModule)
)]
public class BookStoreScratchHttpApiHostModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDbContextOptions>(options =>
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
options.UseNpgsql();
});
context.Services.AddAbpSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "EventHubPublic API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.UseSwagger();
app.UseAbpSwaggerUI(options =>
{
options.RoutePrefix = "swagger";
options.DefaultModelsExpandDepth(-1);
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Public API");
});
app.UseConfiguredEndpoints();
}
}
修改 Program.cs 內容,這裡只留下必要的程式。
using BookStoreScratch.HttpApi.Host;
using Serilog;
using Serilog.Events;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning)
#if DEBUG
.MinimumLevel.Override("EventHub", LogEventLevel.Debug)
#else
.MinimumLevel.Override("EventHub", LogEventLevel.Information)
#endif
.Enrich.FromLogContext()
.WriteTo.Async(c => c.Console())
.CreateLogger();
var builder = WebApplication.CreateBuilder(args);
builder.Host
.UseAutofac()
.UseSerilog();
await builder.Services.AddApplicationAsync<BookStoreScratchHttpApiHostModule>();
var webApplication = builder.Build();
await webApplication.InitializeApplicationAsync();
await webApplication.RunAsync();
return 0;
設定 UnifiedDbContext、UnifiedDbContextFactory、appsettings.json,可以直接從之前的 DbMigrator 專案複製。
using BookStoreScratch.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace BookStoreScratch.HttpApi.Host;
public class UnifiedDbContext : AbpDbContext<UnifiedDbContext>
{
public UnifiedDbContext(DbContextOptions<UnifiedDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ConfigureBookStoreScratch();
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
namespace BookStoreScratch.HttpApi.Host;
public class UnifiedDbContextFactory : IDesignTimeDbContextFactory<UnifiedDbContext>
{
public UnifiedDbContext CreateDbContext(string[] args)
{
// https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<UnifiedDbContext>()
.UseNpgsql(configuration.GetConnectionString("Default"));
return new UnifiedDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"Default": "Host=localhost;Port=5432;Database=BookStoreScratch;User ID=postgres;Password=myPassw0rd;"
}
}
設定完成後將專案運行起來瀏覽器會開啟 swagger 網址(/swagger/index.html)可以看到我們的 Book Curd 成功顯示
- GET /api/book/
- PUT /api/book/
- DELETE /api/book/
- GET /api/book
- POST /api/book
先使用 POST /api/book 新增一筆書籍到資料庫內
{
"name": "mybook",
"bookType": 1,
"publishDate": "2023-07-06",
"price": 10
}
成功插入後會映射 BookDto 後回傳,我們透過實體 id 來進行查詢
{
"name": "mybook",
"type": 1,
"publishDate": "2023-07-06T00:00:00",
"price": 10,
"id": "767085e2-a2c3-d3f5-c37b-3a0c42d2f44e"
}
使用 Get 方法可以正常回傳資料 https://localhost:7225/api/book/767085e2-a2c3-d3f5-c37b-3a0c42d2f44e
{
"name": "mybook",
"type": 1,
"publishDate": "2023-07-06T00:00:00",
"price": 10,
"id": "767085e2-a2c3-d3f5-c37b-3a0c42d2f44e"
}
今天的進度 Github