ABP VNext + .NET Minimal API:极简微服务快速开发

ABP VNext + .NET Minimal API:极简微服务快速开发 💨


📚 目录


1. 引言 🚀

TL;DR ✨

  • 🔥 极简上手 :用 .NET Minimal API + ABP VNext,仅需一个 Program.cs 即可启动轻量级微服务
  • 🛡️ 保留核心能力:依然拥有 ABP 的依赖注入(DI)、AOP 拦截器、配置管理、审计日志等特性
  • 🎉 即开即用:ProblemDetails 异常格式、Swagger/OpenAPI、多版本 API 文档、API 版本管理、健康检查、CORS、多语言本地化一键配置
  • 高性能、高可用:Serilog 日志 + Kestrel 原生优化 + Docker Compose,启动秒级响应

2. 环境与依赖 🛠️

  • .NET SDK:6 +

  • ABP VNext:6.x +

  • 数据库:PostgreSQL(生产)、In-Memory(测试)

  • 关键 NuGet 包

    bash 复制代码
    dotnet add package Volo.Abp.AspNetCore.Builder  
    dotnet add package Volo.Abp.Modularity  
    dotnet add package Volo.Abp.EntityFrameworkCore.PostgreSql  
    dotnet add package Volo.Abp.AutoMapper  
    dotnet add package Volo.Abp.AspNetCore.Serilog        # Serilog 丰富器  
    dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer  
    dotnet add package Swashbuckle.AspNetCore  
    dotnet add package Asp.Versioning.Http  
    dotnet add package Asp.Versioning.Mvc.ApiExplorer      # 版本化 ApiExplorer  
    dotnet add package Hellang.Middleware.ProblemDetails    # ProblemDetails 中间件  
    dotnet add package Serilog  
    dotnet add package Serilog.Sinks.Console               # Serilog 控制台 Sink  
    dotnet add package StackExchange.Redis  

3. 项目结构与入口 📂

复制代码
MyMinimalService/
├─ Program.cs
├─ Modules/
│    └─ MyServiceModule.cs
├─ Application/
│    ├─ Dtos/
│    │    ├─ UserDto.cs
│    │    └─ CreateUserDto.cs
│    └─ IUserAppService.cs
├─ Domain/
│    ├─ Entities/
│    │    └─ User.cs
│    └─ MyDbContext.cs
└─ appsettings.json

4. Program.cs 📝

csharp 复制代码
using System.Text.Json;
using System.Text.Json.Serialization;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Serilog;
using Volo.Abp;
using Volo.Abp.Autofac;
using Volo.Abp.AspNetCore.Builder;     // 确保 AddApplication 扩展可用
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.EventBus;                // 确保 AddAbpEventBus 扩展可用
using Volo.Abp.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// 1. Serilog 全局配置 🔥
builder.Host.UseSerilog((ctx, lc) =>
{
    lc.ReadFrom.Configuration(ctx.Configuration)
      .Enrich.FromLogContext()
      .Enrich.WithProperty("Application", ctx.HostingEnvironment.ApplicationName)
      .WriteTo.Console();
});

// 2. 使用 Autofac 容器,确保 ABP AOP 拦截器生效 🛡️
builder.Host.UseAutofac();

// 3. 注册 ABP 模块化支持(包含 ASP.NET Core 特性)📦
builder.Services.AddApplication<MyServiceModule>();

// 4. 本地化:资源文件路径 🌐
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

// 5. ProblemDetails 异常格式化 ⚠️
builder.Services.AddProblemDetails(opts =>
{
    opts.MapToStatusCode<Exception>(StatusCodes.Status500InternalServerError);
});

// 6. 身份验证与授权 🔒
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options => {
        options.Authority = builder.Configuration["Auth:Issuer"];
        options.Audience  = builder.Configuration["Auth:Audience"];
    });
builder.Services.AddAuthorization();

// 7. 启用 ABP 事件总线(自动注册 AOP 拦截器)🔄
builder.Services.AddAbpEventBus();

// 8. JSON 序列化优化 📲
builder.Services.Configure<JsonOptions>(opts =>
{
    opts.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    opts.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});

// 9. API 版本化 📑
builder.Services.AddApiVersioning(opts =>
{
    opts.AssumeDefaultVersionWhenUnspecified = true;
    opts.DefaultApiVersion = new ApiVersion(1, 0);
    opts.ReportApiVersions = true;
    opts.ApiVersionReader = new UrlSegmentApiVersionReader();
});
builder.Services.AddVersionedApiExplorer(opts =>
{
    opts.GroupNameFormat = "'v'VVV";
    opts.SubstituteApiVersionInUrl = true;
});

// 10. Swagger/OpenAPI 🛠️
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(opts =>
{
    // 在此阶段 BuildServiceProvider 有轻微容器重复,但示例中可用
    var provider = builder.Services.BuildServiceProvider()
                        .GetRequiredService<IApiVersionDescriptionProvider>();
    foreach (var desc in provider.ApiVersionDescriptions)
    {
        opts.SwaggerDoc(desc.GroupName, new OpenApiInfo {
            Title   = "MyMinimalService",
            Version = desc.ApiVersion.ToString()
        });
    }
});

// 11. 健康检查 + Redis 缓存 ❤️
builder.Services.AddHealthChecks()
    .AddDbContextCheck<MyDbContext>("db")
    .AddRedis(builder.Configuration["Redis:ConnectionString"], name: "redis");
builder.Services.AddStackExchangeRedisCache(opts =>
{
    opts.Configuration = builder.Configuration["Redis:ConnectionString"];
});

// 12. CORS 🌐
builder.Services.AddCors(opts =>
{
    opts.AddDefaultPolicy(p =>
        p.AllowAnyOrigin()
         .AllowAnyHeader()
         .AllowAnyMethod());
});

var app = builder.Build();

// 13. 初始化 ABP 应用 🔧
app.InitializeApplication();

中间件管道(推荐顺序)⏱️

ProblemDetails ⚠️ AbpExceptionHandling 🛠️ 本地化 🌐 CORS 🌐 Serilog 请求日志 📋 Authentication 🔒 Authorization 🛡️ AbpSerilogEnrichers 📝 Swagger 🛠️ HealthCheck ❤️

csharp 复制代码
app.UseProblemDetails();                     // RFC7807 异常格式
app.UseAbpExceptionHandling();               // ABP 全局异常处理
app.UseAbpRequestLocalization();             // 本地化
app.UseCors();                               // 跨域
app.UseSerilogRequestLogging();              // Serilog 请求日志
app.UseAuthentication();                     // 验证
app.UseAuthorization();                      // 授权
app.UseAbpSerilogEnrichers();                // ABP Serilog 丰富器
app.UseSwagger();
app.UseSwaggerUI(c =>
{
    var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
    foreach (var desc in provider.ApiVersionDescriptions)
    {
        c.SwaggerEndpoint($"/swagger/{desc.GroupName}/swagger.json", desc.GroupName);
    }
});
app.UseHealthChecks("/health");

// 极简路由定义 📍
app.MapGroup("/api/v{version:apiVersion}/users")
   .MapGet("/{id}", (Guid id, IUserAppService svc) => svc.GetAsync(id))
   .MapGet("/",        (IUserAppService svc) => svc.GetListAsync())
   .MapPost("/",       (CreateUserDto dto, IUserAppService svc) => svc.CreateAsync(dto))
   .RequireAuthorization();

app.Run();

5. 定义 ABP 模块 🏗️

csharp 复制代码
using Volo.Abp;
using Volo.Abp.Autofac;
using Volo.Abp.AspNetCore.Builder;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;

namespace MyMinimalService.Modules
{
    [DependsOn(
        typeof(AbpAspNetCoreModule),       // ASP.NET Core 特性支持
        typeof(AbpAutofacModule),
        typeof(AbpEntityFrameworkCoreModule),
        typeof(AbpAspNetCoreSerilogModule)  // Serilog 丰富器模块
    )]
    public class MyServiceModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            Configure<AbpDbContextOptions>(opts => opts.UseNpgsql());

            context.Services.AddAbpDbContext<MyDbContext>(opts =>
            {
                opts.AddDefaultRepositories(true);
            });

            context.Services.AddAutoMapperObjectMapper<MyServiceModule>();
            Configure<AbpAutoMapperOptions>(opt => opt.AddMaps<MyServiceModule>());
        }
    }
}

6. 实体、DTO 与 DbContext 📦

实体:Domain/Entities/User.cs

csharp 复制代码
using System;
using Volo.Abp.Domain.Entities.Auditing;

namespace MyMinimalService.Domain.Entities
{
    /// <summary>
    /// 用户实体,继承了审计字段(CreationTime、CreatorId、LastModificationTime 等)
    /// </summary>
    public class User : AuditedAggregateRoot<Guid>
    {
        /// <summary>
        /// 用户名
        /// </summary>
        public string Name { get; set; }

        protected User() { }

        public User(Guid id, string name)
            : base(id)
        {
            Name = name;
        }
    }
}

DTO:Application/Dtos/UserDto.cs

csharp 复制代码
using System;

namespace MyMinimalService.Application.Dtos
{
    /// <summary>
    /// 用户数据传输对象
    /// </summary>
    public class UserDto
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }
}

DTO:Application/Dtos/CreateUserDto.cs

csharp 复制代码
using System.ComponentModel.DataAnnotations;

namespace MyMinimalService.Application.Dtos
{
    /// <summary>
    /// 创建用户时的输入 DTO
    /// </summary>
    public class CreateUserDto
    {
        [Required]
        [StringLength(128, MinimumLength = 1)]
        public string Name { get; set; }
    }
}

DbContext:Domain/MyDbContext.cs

csharp 复制代码
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using MyMinimalService.Domain.Entities;

namespace MyMinimalService.Domain
{
    /// <summary>
    /// 应用的 EF Core 上下文,配置了 User 实体和默认仓储
    /// </summary>
    public class MyDbContext : AbpDbContext<MyDbContext>
    {
        public DbSet<User> Users { get; set; }

        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.Entity<User>(b =>
            {
                b.ToTable("AppUsers");                        // 自定义表名
                b.HasKey(x => x.Id);
                b.Property(x => x.Name)
                 .IsRequired()
                 .HasMaxLength(128);
            });
        }
    }
}

7. 应用服务接口与实现 🛠️

接口:Application/IUserAppService.cs

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using MyMinimalService.Application.Dtos;

namespace MyMinimalService.Application
{
    /// <summary>
    /// 定义用户相关的应用服务接口
    /// </summary>
    public interface IUserAppService : IApplicationService
    {
        /// <summary>
        /// 获取指定 Id 的用户
        /// </summary>
        Task<UserDto> GetAsync(Guid id);

        /// <summary>
        /// 获取所有用户列表
        /// </summary>
        Task<List<UserDto>> GetListAsync();

        /// <summary>
        /// 创建一个新用户
        /// </summary>
        Task<UserDto> CreateAsync(CreateUserDto input);
    }
}

实现:Application/UserAppService.cs

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using MyMinimalService.Application.Dtos;
using MyMinimalService.Domain.Entities;

namespace MyMinimalService.Application
{
    /// <summary>
    /// 用户应用服务实现,包含基本的增删改查
    /// </summary>
    public class UserAppService : ApplicationService, IUserAppService
    {
        private readonly IRepository<User, Guid> _repository;

        public UserAppService(IRepository<User, Guid> repository)
        {
            _repository = repository;
        }

        /// <summary>
        /// 根据 Id 获取一个用户,需认证
        /// </summary>
        [Authorize]
        public async Task<UserDto> GetAsync(Guid id)
        {
            var entity = await _repository.GetAsync(id);
            return ObjectMapper.Map<User, UserDto>(entity);
        }

        /// <summary>
        /// 获取所有用户列表
        /// </summary>
        public async Task<List<UserDto>> GetListAsync()
        {
            var entities = await _repository.GetListAsync();
            return entities
                .Select(u => ObjectMapper.Map<User, UserDto>(u))
                .ToList();
        }

        /// <summary>
        /// 创建新用户
        /// </summary>
        public async Task<UserDto> CreateAsync(CreateUserDto input)
        {
            var user = new User(GuidGenerator.Create(), input.Name);
            var created = await _repository.InsertAsync(user);
            return ObjectMapper.Map<User, UserDto>(created);
        }
    }
}

8. AOP 拦截器与调用流程 🔄

客户端请求 ABP 拦截管道 验证拦截器 日志拦截器 业务方法 审计拦截器 返回结果


9. 配置与环境管理 🌍

  • 加载顺序appsettings.jsonappsettings.{环境}.json → 环境变量

  • 示例 appsettings.json

    jsonc 复制代码
    {
      "ConnectionStrings": {
        "Default": "Host=localhost;Database=mydb;Username=user;Password=pass"
      },
      "Redis": {
        "ConnectionString": "localhost:6379"
      },
      "Auth": {
        "Issuer": "https://auth.example.com",
        "Audience": "MyMinimalService"
      }
    }
  • 支持 Kubernetes Secret、Vault 等插件扩展


10. 安全与鉴权 🛡️

csharp 复制代码
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = cfg["Auth:Issuer"];
        options.Audience  = cfg["Auth:Audience"];
    });
app.UseAuthentication();
app.UseAuthorization();
  • .RequireAuthorization()[Authorize] 保护端点
  • 支持 ABP 的 Policy 与 PermissionChecker

11. 中间件与辅助功能 🔧

  • ProblemDetails:统一异常格式
  • AbpExceptionHandling:ABP 全局异常处理
  • Localization:多语言支持
  • Serilog:日志丰富器 + 请求日志
  • Swagger/OpenAPI:多版本文档
  • HealthChecks:健康探针
  • CORS:跨域配置

13. 最佳实践建议 📈

  • DI 生命周期DbContext 保持默认 Scoped

  • Kestrel 调优

    jsonc 复制代码
    "Kestrel": {
      "Limits": { "MaxConcurrentConnections": 1000 }
    }
  • 分布式缓存 :Redis (AddStackExchangeRedisCache) 实现高可用


相关推荐
hdsoft_huge2 小时前
SpringBoot 与 JPA 整合全解析:架构优势、应用场景、集成指南与最佳实践
java·spring boot·架构
Livingbody3 小时前
基于【ERNIE-4.5-VL-28B-A3B】模型的图片内容分析系统
后端
百锦再3 小时前
详细解析 .NET 依赖注入的三种生命周期模式
java·开发语言·.net·di·注入·模式·依赖
你的人类朋友4 小时前
🍃Kubernetes(k8s)核心概念一览
前端·后端·自动化运维
追逐时光者5 小时前
面试第一步,先准备一份简洁、优雅的简历模板!
后端·面试
DoraBigHead5 小时前
你写前端按钮,他们扛服务器压力:搞懂后端那些“黑话”!
前端·javascript·架构
慕木兮人可5 小时前
Docker部署MySQL镜像
spring boot·后端·mysql·docker·ecs服务器
发粪的屎壳郎5 小时前
ASP.NET Core 8 轻松配置Serilog日志
后端·asp.net·serilog
isNotNullX6 小时前
数据中台架构解析:湖仓一体的实战设计
java·大数据·数据库·架构·spark
倔强青铜三6 小时前
苦练Python第4天:Python变量与数据类型入门
前端·后端·python