.NET Aspire自定义容器服务发现失效问题排查
我在.NET Aspire项目中尝试通过客户端连接自定义容器,但服务发现功能无法正常工作。期望ServiceProvider实例化LabseClient时,LabseClient.LabseTest能获取到已完整配置的HttpClient,但实际并未实现——当前配置为默认的80端口,导致无法找到容器内的服务,而它本应被动态配置。
我创建了测试解决方案复现该问题:运行AppHost后,点击apiservice的任一接口即可触发问题(下载容器需耗时,未修改镜像以避免引入额外问题)。
相关代码说明
1. Ainizeml.Labse.Hosting项目(自定义容器封装)
该项目用于封装容器并实现IResourceWithConnectionString接口,代码如下:
LabseResource类:
public sealed class LabseResource(string name) : ContainerResource(name), IResourceWithConnectionString { internal const string HttpEndpointName = "http"; internal const int LabseRestAPIPort = 8501; private EndpointReference? _httpReference; public EndpointReference HttpEndpoint => _httpReference ??= new(this, HttpEndpointName); public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create( $"http://{HttpEndpoint.Property(EndpointProperty.Host)}:{HttpEndpoint.Property(EndpointProperty.Port)}" ); }
扩展方法:
public static class LabseResourceBuilderExtensions { public static IResourceBuilder<LabseResource> AddLabse( this IDistributedApplicationBuilder builder, string name, int? httpPort = null) { var resource = new LabseResource(name); return builder.AddResource(resource) .WithImage(LabseContainerImageTags.Image) .WithImageRegistry(LabseContainerImageTags.Registry) .WithImageTag(LabseContainerImageTags.Tag) .WithHttpEndpoint( targetPort: LabseResource.LabseRestAPIPort, port: httpPort, name: LabseResource.HttpEndpointName); } }
镜像标签常量:
internal static class LabseContainerImageTags { internal const string Registry = "docker.io"; internal const string Image = "ainizeml/labse"; internal const string Tag = "latest"; }
2. Aspire.ServiceDiscovery.AppHost的Program.cs
仅定义容器与项目,并建立关联:
using Ainizeml.Labse.Hosting; var builder = DistributedApplication.CreateBuilder(args); var labseModel = builder.AddLabse("labse"); var apiService = builder.AddProject<Projects.Aspire_ServiceDiscovery_WebAPI>("apiservice") .WithReference(labseModel) .WaitFor(labseModel); builder.Build().Run();
3. Aspire.ServiceDiscovery.WebAPI项目
LabseClient类:
internal class LabseClient(HttpClient httpClient) { public async Task<string> LabseTest() { try { var response = await httpClient.GetAsync("/v1/models/ainize/metadata"); return response.ToString(); } catch (Exception ex) { return ex.Message; } } }
WebAPI的Program.cs配置:
var builder = WebApplication.CreateBuilder(args); builder.AddServiceDefaults(); builder.Services.AddProblemDetails(); builder.Services.AddOpenApi(); builder.Services.AddHttpClient<LabseClient>( static (client) => { client.BaseAddress = new Uri("http://labse"); } ); var app = builder.Build(); app.UseExceptionHandler(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } app.MapGet("/", (LabseClient labseClient) => labseClient.LabseTest()); app.MapDefaultEndpoints(); app.Run();
问题原因及修复方案
问题根源
你手动硬编码了HttpClient的BaseAddress为http://labse,但.NET Aspire的服务发现需要通过配置绑定来动态注入正确的服务地址(包含正确端口),而不是直接写死地址。硬编码的地址会被解析为默认80端口,导致无法访问容器内的8501端口服务。
修复步骤
修改WebAPI的Program.cs,移除硬编码的BaseAddress,改用配置绑定
将AddHttpClient的代码替换为:builder.Services.AddHttpClient<LabseClient>((sp, client) => { var config = sp.GetRequiredService<IConfiguration>(); var labseUrl = config.GetConnectionString("labse"); client.BaseAddress = new Uri(labseUrl ?? throw new InvalidOperationException("Labse connection string not found")); });或者使用更简洁的写法:
builder.Services.AddHttpClient<LabseClient>() .ConfigureHttpClient((sp, client) => { client.BaseAddress = new Uri(sp.GetRequiredService<IConfiguration>().GetConnectionString("labse")!); });确认
LabseResource配置正确
你的LabseResource中ConnectionStringExpression已经正确使用了HttpEndpoint的Host和Port属性,这部分无需修改——Aspire会在运行时自动将其替换为容器的实际地址和端口(比如http://labse:8501)。验证关联配置
AppHost中调用.WithReference(labseModel)时,Aspire会自动为apiservice项目注入名为labse的连接字符串,值就是ConnectionStringExpression生成的完整地址。
为什么之前的方式失效?
硬编码http://labse时,HttpClient会默认使用80端口,而Aspire的服务发现机制不会自动替换硬编码的地址,必须通过读取配置中的连接字符串才能获取动态生成的正确服务地址。
内容的提问来源于stack exchange,提问作者Miklós Pathy

