ABP Framework-从零搭建
目录
Version
7.4.1
创建空Web项目
dotnet new sln
dotnew new web --name AspNetCoreAbp
dotnet sln add .\AspNetCoreAbp
增加Abp的基础包
Volo.Abp.AspNetCore.Mvc
增加启动模块
考虑是使用WebApi模式,如下代码中就尽可能的精简,去除Mvc部分,Abp没有纯WebApi依赖模块,只有AbpAspNetCoreMvcModule模块。
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity;
namespace AspNetCoreAbp;
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
public class AspNetCoreAbpModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
app.UseRouting();
app.UseConfiguredEndpoints();
}
}
更改Program.cs
在7.0以上,ABP不用再单独弄一个Startup.cs文件,直接在Program.cs中启动模块就行。
默认代码
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
变更后代码
var builder = WebApplication.CreateBuilder(args);
builder.AddApplicationAsync<AppModule>();
var app = builder.Build();
app.InitializeApplicationAsync();
app.Run();
就第2行和第5行代码有所改动,也正对应着原Startup中的两行代码。
// adds all services defined in all modules starting from the AppModule.
builder.AddApplicationAsync<AppModule>();
//initializes and starts the application.
app.InitializeApplicationAsync();
集成Autofac
AspNetCore自身的DI系统已经足够使用了,但Autofac提供了更多功能,像属性注入,方法注入等,ABP需要依靠这些完成更丰富的功能。
增加Autofac的VoloAbp的包
Volo.Abp.Autofac
启动模块依赖AutofacModule
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAutofacModule)
)]
public class AspNetCoreAbpModule : AbpModule
{
...
}
Program.cs中替换DI容器,只需要使用UseAutofac注册到builder中,及其简单。
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseAutofac();
await builder.AddApplicationAsync<AspNetCoreAbpModule>();
集成Swagger
当前项目中不具备Swagger的展示,启动项目看不到接口文档,集成Swagger进入到项目中。
https://docs.abp.io/en/abp/latest/API/Swagger-Integration
增加Swagger的VoloAbp的包
Volo.Abp.Swashbuckle
启动模块依赖SwaggerModule
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAutofacModule),
typeof(AbpSwashbuckleModule)
)]
public class AspNetCoreAbpModule : AbpModule
{
...
}
Swagger服务注册,在启动模块中增加ConfigurationServices,其中注册SwaggerGen
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
services.AddAbpSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "AspNetCoreAbp API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
}
在中间件中增加SwaggerUI的中间件,同时需要UseStaticFiles中间件及UseSwagger中间件。
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
app.UseStaticFiles();
app.UseSwagger();
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "AspNetCoreAbp API");
});
app.UseRouting();
app.UseConfiguredEndpoints();
}
启动后是没有接口的,需要先增加一个Controller测试下。此处父类使用ControllerBase或是AbpControllerBase都行,无影响。
using Microsoft.AspNetCore.Mvc;
namespace AspNetCoreAbp.Controllers;
[ApiController]
[Route("api/[controller]")]
public class HomeController : ControllerBase
{
[HttpGet]
public int Get()
{
return 0;
}
}
启动后可以看到接口信息
如需要屏蔽Abp自身接口,可以关闭即可
https://docs.abp.io/en/abp/latest/API/Swagger-Integration#hide-abp-endpoints-on-swagger-ui
如需要集成OAuth到Swagger中,以跳转到Auth拿Token可以按照如下设置
https://docs.abp.io/en/abp/latest/API/Swagger-Integration#using-swagger-with-oidc
https://docs.abp.io/en/abp/latest/API/Swagger-Integration#using-swagger-with-oidc
如需要带着Token请求接口,可以做些改动在options上增加AddSecurityDefinition和AddSecurityRequirement.
https://dev.to/eduardstefanescu/aspnet-core-swagger-documentation-with-bearer-authentication-40l6
services.AddAbpSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "AspNetCoreAbp API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
// 使用Bearer token,如下默认格式就行,无需改动参数
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 1safsfsdfdfd\"",
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
再次启动便可以输入Token,请求接口了。
注意
- 在SwaggerGen中的配置,是对于展示部分的配置
- 在中间件的配置,是下拉展示的配置
右上角对应选择项,也就使用对应的swagger.json文件
两者命名可以不同,但是也建议设置成相同。
集成Authentication
鉴权部分考虑集成JwtBearer,在Abp中,鉴权部分并没有文档描述,因为使用的就是AspNetCore自身的鉴权体系,只是封装了几个包
此处只关注JwtBearer的包,内部封装了(Microsoft.AspNetCore.Authentication.JwtBearer),来方便同步版本。
增加Authentication的VoloAbp包
Volo.Abp.Authentication
启动模块依赖JwtBearerModule
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAutofacModule),
typeof(AbpSwashbuckleModule),
typeof(AbpAspNetCoreAuthenticationJwtBearerModule)
)]
public class AspNetCoreAbpModule : AbpModule
{
...
}
其余配置和在AspNetCore下鉴权服务配置相同。可参照如下链接设置。 https://sandrino.dev/blog/aspnet-core-5-jwt-authorization
服务注册中增加Authentication和JwtBearer,此处只简单添加不做具体参数设置。
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = false,
SignatureValidator = (string token, TokenValidationParameters parameters) => new JwtSecurityToken(token)
};
});
在中间件中使用鉴权授权中间件,如下第14,15行,注意需要在UseRouting之后。
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
app.UseStaticFiles();
app.UseSwagger();
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "AspNetCoreAbp API");
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseConfiguredEndpoints();
}
集成Serilog
增加Serilog的VoloAbp包
Volo.Abp.AspNetCore.Serilog
这个包中依赖了Serilog,所以我们无需在额外去装Serilog包了。https://github.com/serilog/serilog/wiki/Getting-Started
启动模块依赖SerilogModule
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAutofacModule),
typeof(AbpSwashbuckleModule),
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
typeof(AbpAspNetCoreSerilogModule)
)]
public class AspNetCoreAbpModule : AbpModule
{
...
}
中间件注册中,可以设置AbpSerilogEnrichers,如下第14行代码,能够输出当前用户,租户,client等额外信息。此为可选项,不是必备的。
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
app.UseStaticFiles();
app.UseSwagger();
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "AspNetCoreAbp API");
});
app.UseRouting();
app.UseAbpSerilogEnrichers();
app.UseAuthentication();
app.UseAuthorization();
app.UseConfiguredEndpoints();
}
更改Program.cs
需要在启动时候替换Logger,用上Serilog的。
额外安装Serilog两个包
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
原先的Program.cs内容如下
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseAutofac();
await builder.AddApplicationAsync<AspNetCoreAbpModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
在此基础上封装成一个方法,并增加机密文件及Serilog的替换,其余保持不变。
private static async Task Init(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Host.AddAppSettingsSecretsJson();
builder.Host.UseAutofac();
builder.Host.UseSerilog();
await builder.AddApplicationAsync<AspNetCoreAbpModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
}
并在此基础上在封装一个类,用来承载启动日志,Main方法中为默认格式,如果要搭建新项目,直接使用默认格式即可,注意日志文件写入后,需要将日志文件加入到gitignore中。
public class Program
{
public async static Task<int> Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
try
{
Log.Information("Starting AspNetCoreAbp.");
await Init(args);
return 0;
}
catch (Exception ex)
{
if (ex is HostAbortedException)
{
throw;
}
Log.Fatal(ex, "Host terminated unexpectedly!");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
private static async Task Init(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Host.AddAppSettingsSecretsJson();
builder.Host.UseAutofac();
builder.Host.UseSerilog();
await builder.AddApplicationAsync<AspNetCoreAbpModule>();
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
}
}
设置Serilog从配置文件中读取日志配置
可以设置builder.Host.UseSerilog(),增加参数让Serilog读取appsetings.json中的配置,当然默认的Logging配置可以删掉了,因为Serilog不会用到那段配置设置,而是走自己的配置设置。
https://github.com/serilog/serilog-settings-configuration
设置Serilog输出渠道
https://github.com/serilog/serilog/wiki/Configuration-Basics
serilog能够将日志信息输出到很多个渠道,Serilog.AspNetCore包中已经集成了几个渠道,因此选择渠道时候可以先看下已有包。
配置AspNetCoreMvc
全局AspNetCoreMvc设置,基本不用设置,也可以用来自动生成Controller,如下设置Application模块生成Controller,并设置路由前缀
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(OrderingManagementApplicationModule).Assembly, opts =>
{
opts.RootPath = "OrderingManagement1";
});
});
生成后,Swagger中会带上这节路由地址。
配置Mvc
Mvc中间件相关的设置,基本不用设置
Configure<MvcOptions>(options =>
{
var filterMetadata = options.Filters.FirstOrDefault(x => x is ServiceFilterAttribute attribute && attribute.ServiceType.Equals(typeof(AbpExceptionFilter)));
options.Filters.Remove(filterMetadata);
options.Filters.Add(typeof(FixtExceptionFilter));
options.Filters.Add(new Infrastructure.FixtAuthorizeFilter());
});
配置EFCore&DbContext
增加EFCore的VoloAbp包
选择底层的存储介质包
此处选择Mysql的包,第一个包在各存储介质包中都有依赖,因此不用再引用第一个基础包。
Volo.Abp.EntityFrameworkCore.MySQL
增加Domain文件夹或类库
此处增加Domain文件夹,新增Order实体。
using Volo.Abp.Domain.Entities;
namespace AspNetCoreAbp.Domains.Models;
public class Order : BasicAggregateRoot<Guid>
{
public string Name { get; set; } = default!;
}
增加EfCore文件夹或类库
此处增加EFCore文件夹,新增DbContext,配置Order的模型。
using AspNetCoreAbp.Domains.Models;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace AspNetCoreAbp.EntityFrameworkCores;
public class AppDbContext : AbpDbContext<AppDbContext>
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
//Always call the base method
base.OnModelCreating(builder);
builder.Entity<Order>(o =>
{
o.ToTable("Orders");
o.ConfigureByConvention(); //auto configure for the base class props
o.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
}
}
启动模块依赖Module
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAutofacModule),
typeof(AbpSwashbuckleModule),
typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
typeof(AbpAspNetCoreSerilogModule),
typeof(AbpEntityFrameworkCoreMySQLModule)
)]
public class AspNetCoreAbpModule : AbpModule
{
...
}
服务注册中增加DbContext的注册,同时使用默认仓储服务。
public override void ConfigureServices(ServiceConfigurationContext context)
{
...
services.AddAbpDbContext<AppDbContext>(options =>
{
options.AddDefaultRepositories();
});
...
}
如果是使用的类库,那么一般是在类库中有一个module,注册写在module中,Host层依赖类库中的module。
[DependsOn(typeof(AbpEntityFrameworkCoreModule))]
public class AspNetCoreEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<AppDbContext>(options =>
{
options.AddDefaultRepositories();
});
}
}
服务注册中指明存储介质,默认使用的是ConnectionStrings:Default,此处不再做额外配置,一切从简,从默认。
public override void ConfigureServices(ServiceConfigurationContext context)
{
...
Configure<AbpDbContextOptions>(options =>
{
options.UseMySQL();
});
...
}
配置文件中增加连接字符串
{
"ConnectionStrings": {
"Default": "Server=ip;Database=dbname;Port=3306;charset=utf8;uid=root;pwd=xxx;"
}
}
安装efcore tool和design包
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.14">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.14">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
ef命令生成脚本
add-migration xxx
update-database
新建OrderController测试存储
using AspNetCoreAbp.Domains.Models;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Domain.Repositories;
namespace AspNetCoreAbp.Controllers;
[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
private readonly IRepository<Order,Guid> _orderRepository;
public OrderController(IRepository<Order, Guid> orderRepository)
{
_orderRepository = orderRepository;
}
[HttpGet]
public async Task<List<Order>> GetListAsync()
{
return await _orderRepository.GetListAsync();
}
[HttpPost]
public async Task<Order> CreateAsync(string name)
{
return await _orderRepository.InsertAsync(new Order()
{
Name = name
});
}
}
2024-01-15,望技术有成后能回来看见自己的脚步。