如何结合appSettings配置值使用Host特性限制API端点
解决动态配置Host限制的方案
因为C#特性的参数必须是编译时常量,所以直接在[Host]特性里引用从配置读取的动态值是行不通的。下面提供两种可替代的实现方式,都能从appsettings.json动态获取允许的主机地址,且支持不同环境的配置隔离。
方案一:自定义授权策略(推荐,贴合ASP.NET Core授权模型)
这种方式可以把主机校验逻辑整合到授权体系中,适合需要和其他授权规则结合的场景。
1. 配置与绑定(复用你已有的配置类)
首先确保配置类和appsettings.json的绑定正确:
// 配置类 public class AllowedDomains { public string[] AllowedHosts { get; set; } = Array.Empty<string>(); }
appsettings.json配置:
"Cors": { "AllowedOrigins": "https://your-prod-domain.com,https://staging-domain.com" }
appsettings.Development.json(本地环境单独配置):
"Cors": { "AllowedOrigins": "localhost" }
在Program.cs中绑定配置:
builder.Services.Configure<AllowedDomains>(options => { var origins = builder.Configuration["Cors:AllowedOrigins"]; options.AllowedHosts = origins?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>(); });
2. 创建自定义授权要求与处理器
// 空的授权要求,仅作为标识 public class AllowedHostsRequirement : IAuthorizationRequirement { } // 授权处理器,负责校验请求主机 public class AllowedHostsAuthorizationHandler : AuthorizationHandler<AllowedHostsRequirement> { private readonly IOptions<AllowedDomains> _allowedDomains; private readonly IHttpContextAccessor _httpContextAccessor; public AllowedHostsAuthorizationHandler(IOptions<AllowedDomains> allowedDomains, IHttpContextAccessor httpContextAccessor) { _allowedDomains = allowedDomains; _httpContextAccessor = httpContextAccessor; } protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AllowedHostsRequirement requirement) { var httpContext = _httpContextAccessor.HttpContext; if (httpContext == null) { context.Fail(); return Task.CompletedTask; } // 获取请求主机(不含端口,若需要校验完整主机+端口可改用Host.ToString()) var requestHost = httpContext.Request.Host.Host; var allowedHosts = _allowedDomains.Value.AllowedHosts; if (allowedHosts.Contains(requestHost, StringComparer.OrdinalIgnoreCase)) { context.Succeed(requirement); } else { context.Fail(); } return Task.CompletedTask; } }
3. 注册授权服务
在Program.cs中添加:
// 注册授权策略 builder.Services.AddAuthorization(options => { options.AddPolicy("AllowedHostsPolicy", policy => policy.Requirements.Add(new AllowedHostsRequirement())); }); // 注册处理器与HttpContext访问器 builder.Services.AddScoped<IAuthorizationHandler, AllowedHostsAuthorizationHandler>(); builder.Services.AddHttpContextAccessor();
4. 在接口上使用
对未认证的接口(如注册、密码重置),同时标记[AllowAnonymous]和自定义策略:
[AllowAnonymous] [Authorize(Policy = "AllowedHostsPolicy")] [HttpPost("register")] public IActionResult Register(RegisterModel model) { // 注册逻辑 return Ok(); }
方案二:自定义中间件(更轻量,适合批量路由)
如果不需要和授权体系结合,直接用中间件在请求管道中校验主机,性能更优,也适合批量处理特定路由组。
1. 配置与绑定(同方案一)
2. 创建中间件
public class AllowedHostsMiddleware { private readonly RequestDelegate _next; private readonly IOptions<AllowedDomains> _allowedDomains; public AllowedHostsMiddleware(RequestDelegate next, IOptions<AllowedDomains> allowedDomains) { _next = next; _allowedDomains = allowedDomains; } public async Task InvokeAsync(HttpContext context) { var requestHost = context.Request.Host.Host; var allowedHosts = _allowedDomains.Value.AllowedHosts; if (!allowedHosts.Contains(requestHost, StringComparer.OrdinalIgnoreCase)) { context.Response.StatusCode = StatusCodes.Status403Forbidden; await context.Response.WriteAsync("Host not allowed"); return; } await _next(context); } } // 扩展方法简化注册 public static class AllowedHostsMiddlewareExtensions { public static IApplicationBuilder UseAllowedHostsMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<AllowedHostsMiddleware>(); } }
3. 注册并应用中间件
在Program.cs中,可选择全局应用或仅对特定路由组应用:
// 全局应用(所有请求都校验主机) app.UseAllowedHostsMiddleware(); // 或仅对未认证的公共接口组应用 app.MapGroup("/api/public") .UseAllowedHostsMiddleware() .MapPost("/register", (RegisterModel model) => Results.Ok()) .MapPost("/reset-password", (ResetPasswordModel model) => Results.Ok());
两种方案对比
- 授权策略:更灵活,可与其他授权规则组合,适合需要精细控制单个接口的场景。
- 中间件:性能更高,适合批量处理路由,代码更简洁。
内容的提问来源于stack exchange,提问作者WhatsThePoint
相关产品推荐
相关产品推荐

