You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 07:15:04