.NET Core 6 WebAPI:Program.cs读取请求头及按租户ID配置数据库连接
.NET Core 6 WebAPI 两个需求实现方案
1. 在Program.cs中读取请求头信息
Program.cs是应用的启动配置入口,启动阶段无法直接获取请求头(此时还没有HTTP请求进入)。如果要在请求处理流程中读取请求头,可以通过注册中间件的方式在Program.cs中实现:
var builder = WebApplication.CreateBuilder(args); // 注册基础服务 builder.Services.AddControllers(); // 注册HttpContext访问器,供后续服务读取请求上下文 builder.Services.AddHttpContextAccessor(); var app = builder.Build(); // 配置中间件,在请求管道中读取请求头 app.Use(async (context, next) => { // 读取指定请求头,例如tenant-id if (context.Request.Headers.TryGetValue("tenant-id", out var tenantId)) { // 将租户ID存入HttpContext.Items,供后续服务/中间件使用 context.Items["TenantId"] = tenantId.ToString(); // 调试场景下可直接输出 Console.WriteLine($"当前请求租户ID: {tenantId}"); } // 执行后续中间件 await next(); }); app.UseAuthorization(); app.MapControllers(); app.Run();
如果需要在业务服务中读取请求头,可通过注入IHttpContextAccessor实现:
public class TenantService { private readonly IHttpContextAccessor _httpContextAccessor; public TenantService(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public string GetCurrentTenantId() { var httpContext = _httpContextAccessor.HttpContext; if (httpContext == null) return string.Empty; httpContext.Request.Headers.TryGetValue("tenant-id", out var tenantId); return tenantId.ToString(); } }
2. 根据租户ID动态配置数据库连接字符串
方案一:在DbContext中动态切换连接
首先在appsettings.json中配置多租户连接字符串:
"ConnectionStrings": { "Tenant001": "Server=localhost;Database=Tenant001;Trusted_Connection=True;", "Tenant002": "Server=localhost;Database=Tenant002;Trusted_Connection=True;", "Default": "Server=localhost;Database=DefaultTenant;Trusted_Connection=True;" }
自定义DbContext,通过IHttpContextAccessor获取租户ID,动态设置连接字符串:
public class AppDbContext : DbContext { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IConfiguration _configuration; public AppDbContext(IHttpContextAccessor httpContextAccessor, IConfiguration configuration) { _httpContextAccessor = httpContextAccessor; _configuration = configuration; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // 优先从HttpContext.Items取租户ID(中间件已存入),其次直接读请求头 var tenantId = _httpContextAccessor.HttpContext?.Items["TenantId"]?.ToString() ?? _httpContextAccessor.HttpContext?.Request.Headers["tenant-id"].ToString(); // 根据租户ID匹配连接字符串,无匹配则用默认 var connectionString = _configuration.GetConnectionString(tenantId) ?? _configuration.GetConnectionString("Default"); optionsBuilder.UseSqlServer(connectionString); } // 定义实体DbSet public DbSet<User> Users { get; set; } }
在Program.cs中注册DbContext:
builder.Services.AddDbContext<AppDbContext>();
方案二:DbContext工厂模式(高并发场景推荐)
如果需要更灵活的DbContext实例控制,可使用IDbContextFactory<T>:
public class AppDbContextFactory : IDbContextFactory<AppDbContext> { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IConfiguration _configuration; public AppDbContextFactory(IHttpContextAccessor httpContextAccessor, IConfiguration configuration) { _httpContextAccessor = httpContextAccessor; _configuration = configuration; } public AppDbContext CreateDbContext() { var tenantId = _httpContextAccessor.HttpContext?.Request.Headers["tenant-id"].ToString(); var connectionString = _configuration.GetConnectionString(tenantId) ?? _configuration.GetConnectionString("Default"); var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>(); optionsBuilder.UseSqlServer(connectionString); return new AppDbContext(optionsBuilder.Options); } }
在Program.cs中注册工厂:
builder.Services.AddDbContextFactory<AppDbContext, AppDbContextFactory>();
在控制器中使用:
[ApiController] [Route("api/[controller]")] public class UsersController : ControllerBase { private readonly IDbContextFactory<AppDbContext> _dbContextFactory; public UsersController(IDbContextFactory<AppDbContext> dbContextFactory) { _dbContextFactory = dbContextFactory; } [HttpGet] public async Task<IActionResult> GetUsers() { using var dbContext = _dbContextFactory.CreateDbContext(); var users = await dbContext.Users.ToListAsync(); return Ok(users); } }
关键注意事项
- 统一请求头参数名(例如
tenant-id),HTTP头不区分大小写,但代码中尽量保持一致。 - 必须处理租户ID不存在的情况,配置默认连接字符串避免数据库连接失败。
- 生产环境建议增加租户ID合法性校验,防止非法请求访问未授权数据库。
内容的提问来源于stack exchange,提问作者Dinesh P
相关产品推荐
相关产品推荐

