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

ASP.NET Core反向代理与API网关场景下如何生成正确的绝对链接

解决方案

方案1:透传X-Forwarded-Prefix + 自定义IUrlHelper装饰器(最推荐,兼容.NET5/.NET6+,无业务侵入)

这个方案不需要调整现有路由规则,完全不用改业务代码,技术端点和业务API路径天然隔离。

步骤1:配置ForwardedHeaders处理路径前缀

在ConfigureServices中添加ForwardedHeaders配置,支持网关透传的路径前缀:

using Microsoft.AspNetCore.HttpOverrides;

services.Configure<ForwardedHeadersOptions>(options =>
{
    // 同时处理域名、协议、路径前缀的转发头
    options.ForwardedHeaders = ForwardedHeaders.XForwardedHost 
                               | ForwardedHeaders.XForwardedProto 
                               | ForwardedHeaders.XForwardedPathBase;
    // 添加工厂网关IP到信任列表,防止请求头伪造
    options.KnownProxies.Add(IPAddress.Parse("你的网关服务IP"));
});

在Configure方法的最开头注册ForwardedHeaders中间件(优先级高于所有其他中间件),如果只有一个发布网关,也可以硬编码路径前缀:

// 优先用网关透传的方式,多网关发布时不需要改代码
app.UseForwardedHeaders();

// 单网关场景硬编码前缀的话用这个,不用网关透传X-Forwarded-Prefix
// app.UsePathBase("/business-area/api-name/v1");

要求网关在转发请求时添加请求头:X-Forwarded-Prefix: /business-area/api-name/v1,ASP.NET Core会自动将该值设置为Request.PathBase,生成链接时会自动拼接该前缀。

步骤2:替换默认IUrlHelper工厂,自动去掉/api前缀

通过装饰器模式包装系统默认的IUrlHelper实现,自动移除生成链接中的/api前缀,不需要修改任何业务代码:
首先实现IUrlHelper装饰器:

public class GatewayUrlHelper : IUrlHelper
{
    private readonly IUrlHelper _inner;
    private const string ApiPrefix = "/api";

    public GatewayUrlHelper(IUrlHelper inner) => _inner = inner;
    public ActionContext ActionContext => _inner.ActionContext;
    public string Content(string contentPath) => _inner.Content(contentPath);
    public bool IsLocalUrl(string url) => _inner.IsLocalUrl(url);

    public string Action(UrlActionContext actionContext)
    {
        var url = _inner.Action(actionContext);
        return url?.StartsWith(ApiPrefix) == true ? url[ApiPrefix.Length..] : url;
    }

    public string RouteUrl(UrlRouteContext routeContext)
    {
        var url = _inner.RouteUrl(routeContext);
        return url?.StartsWith(ApiPrefix) == true ? url[ApiPrefix.Length..] : url;
    }

    public string Link(string routeName, object values)
    {
        var url = _inner.Link(routeName, values);
        return url?.Replace($"{ApiPrefix}/", string.Empty) ?? url;
    }
}

然后实现自定义IUrlHelperFactory,替换默认实现:

using Microsoft.AspNetCore.Mvc.Routing;

public class GatewayUrlHelperFactory : IUrlHelperFactory
{
    private readonly IUrlHelperFactory _innerFactory = new EndpointRoutingUrlHelperFactory();
    public IUrlHelper GetUrlHelper(ActionContext context)
    {
        var innerHelper = _innerFactory.GetUrlHelper(context);
        return new GatewayUrlHelper(innerHelper);
    }
}

最后在ConfigureServices中注册替换:

services.AddSingleton<IUrlHelperFactory, GatewayUrlHelperFactory>();

配置完成后,所有通过IUrlHelper生成的链接会自动拼接网关前缀、去掉内部/api前缀,/health、/swagger等技术端点路径不受影响。

方案2:.NET6+路由组方案(升级后可选)

如果升级到.NET6+,可以用顶层路由组功能,将业务API和技术端点完全拆分,不需要自定义IUrlHelper:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHealthChecks();

var app = builder.Build();

// 技术端点直接注册在根路径
app.MapSwagger();
app.MapSwaggerUI(c => c.RoutePrefix = "swagger");
app.MapHealthChecks("/health");

// 业务API统一注册到/api前缀的路由组
var apiGroup = app.MapGroup("/api");
apiGroup.MapControllers();

// 配置网关路径前缀
app.UsePathBase("/business-area/api-name/v1");
app.UseForwardedHeaders();

这种方式生成链接时自动适配网关路径,也不会出现路径混淆的问题。

原中间件方案失效原因

IUrlHelper生成链接时基于路由模板的配置,而非当前请求的Path属性,你在中间件中修改Request.Path不会影响路由模板的解析逻辑,放在管道前面会破坏路由匹配是因为修改后的Path没有/api前缀,找不到对应控制器路由。

内容的提问来源于stack exchange,提问作者Palec

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 19:24:02