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

非IIS环境下实现多个.NET6 AspNetCore WebApi同端口部署

.NET 6 迁移单端口多虚拟路径 OWIN 服务问题解决方案

问题背景

我们有一批基于.NET 4.7.2的WebApi,通过Microsoft.Owin.Hosting包托管(非IIS环境),所有程序及Web控制器均监听同一端口但不同虚拟路径,例如http://ourserver:4711/ServiceA/和http://ourserver:4711/ServiceB/。.NET4.7.2的OWIN包会在首个服务启动时创建内存主机,其余服务仅注册虚拟路径即可正常路由,采用单端口是出于防火墙规则及负载均衡等需求。

现计划迁移至.NET6,虽依赖Microsoft.AspNetCore.Owin包,但无法正常运行。测试使用示例WheatherForecastController,当前有两个核心目标:

  • 目标1:实现服务监听http://localhost:4711/ServiceA/,而非根路径http://localhost:4711
  • 目标2:启动第二个独立exe,使其监听http://localhost:4711/ServiceB/

当前尝试的基础配置代码如下(多种调整后仍未达预期):

public static void Main()
{
    var host = new WebHostBuilder()
        .UseUrls("http://localhost:4711/")        
        .UseKestrel()
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

public sealed class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddMvc(sa =>
        {
            sa.EnableEndpointRouting = false;
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UsePathBase("/ServiceA")
           .Use((context, next) =>
           {
               context.Request.PathBase = "/ServiceA";
               return next();
           })
           .UseRouting()
           .UseMvc()
           .UseOwin();
    }
}

核心说明

.NET 6的Kestrel不支持多独立进程共享同一端口并各自绑定不同虚拟路径,这和.NET Framework下OWIN内存主机的机制完全不同。要实现单端口多虚拟路径的需求,需调整架构,以下是两种可行方案:

方案一:YARP反向代理(生产环境推荐)

通过YARP(微软官方反向代理库)搭建主网关进程,对外暴露统一端口,将不同路径的请求转发到本地不同端口的子服务,既满足单端口需求,又保证服务隔离性。

  1. 主网关项目配置
var builder = WebApplication.CreateBuilder(args);

// 添加YARP服务
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();

app.MapReverseProxy();

app.Run("http://localhost:4711");

配置文件appsettings.json:

{
  "ReverseProxy": {
    "Routes": {
      "serviceA": {
        "ClusterId": "serviceA",
        "Match": {
          "Path": "/ServiceA/{**catch-all}"
        },
        "Transforms": [
          { "PathRemovePrefix": "/ServiceA" }
        ]
      },
      "serviceB": {
        "ClusterId": "serviceB",
        "Match": {
          "Path": "/ServiceB/{**catch-all}"
        },
        "Transforms": [
          { "PathRemovePrefix": "/ServiceB" }
        ]
      }
    },
    "Clusters": {
      "serviceA": {
        "Destinations": {
          "serviceA": {
            "Address": "http://localhost:5001/"
          }
        }
      },
      "serviceB": {
        "Destinations": {
          "serviceB": {
            "Address": "http://localhost:5002/"
          }
        }
      }
    }
  }
}
  1. ServiceA项目配置
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

var app = builder.Build();

app.UseRouting();
app.MapControllers();

app.Run("http://localhost:5001");

控制器添加基础路由:

[Route("api/[controller]")]
[ApiController]
public class WeatherForecastController : ControllerBase
{
    // 业务代码
}

访问路径:http://localhost:4711/ServiceA/api/WeatherForecast

  1. ServiceB项目配置
    与ServiceA逻辑一致,仅需将监听端口改为http://localhost:5002,通过网关的/ServiceB路径访问。

方案二:端口共享+路径过滤(测试环境可用)

通过Kestrel的端口共享特性,让多进程监听同一端口,再通过中间件过滤处理各自路径的请求。但该方案存在请求分配随机的问题,稳定性不足,仅适合测试场景。

  1. ServiceA项目配置
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

// 启用端口共享
builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(4711, listenOptions =>
    {
        listenOptions.UseConnectionLogging();
        listenOptions.UsePortSharing();
    });
});

var app = builder.Build();

// 仅处理/ServiceA开头的请求
app.UseWhen(context => context.Request.Path.StartsWithSegments("/ServiceA"), appBuilder =>
{
    appBuilder.UsePathBase("/ServiceA");
    appBuilder.UseRouting();
    appBuilder.MapControllers();
});

app.Run();
  1. ServiceB项目配置
    仅需将路径过滤逻辑改为/ServiceB:
// ...
app.UseWhen(context => context.Request.Path.StartsWithSegments("/ServiceB"), appBuilder =>
{
    appBuilder.UsePathBase("/ServiceB");
    appBuilder.UseRouting();
    appBuilder.MapControllers();
});
// ...

注意事项

  • .NET 6中OWIN集成仅能通过UseOwin中间件实现,但多进程共享端口的机制与.NET Framework完全不同,无法直接复用旧模式;
  • 反向代理方案是生产环境的最优选择,兼顾单端口需求与服务稳定性;
  • 端口共享方案存在请求分配随机的风险,不建议用于生产环境。

内容的提问来源于stack exchange,提问作者Dee J. Doena

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 13:35:25