BlazorExtensionsAspire solves the problem of automatically injecting the URL of WebAPI into Blazor WebAssembly .
The code for obtaining writes into appsettings.json of BlazorWebAssembly
public static IResourceBuilder<ProjectResource> AddWebAssemblyProject<TProject>( this IDistributedApplicationBuilder builder, string name, IResourceBuilder<ProjectResource> api) where TProject : Aspire.Hosting.IProjectMetadata, new() { var nameOfParameter = api.Resource.Name + "_host"; var projectBuilder = builder.AddProject<TProject>(name); var p = new TProject(); string hostApi = p.ProjectPath; var dir = Path.GetDirectoryName(hostApi); ArgumentNullException.ThrowIfNull(dir); var wwwroot = Path.Combine(dir, "wwwroot"); if (!Directory.Exists(wwwroot)) { Directory.CreateDirectory(wwwroot); } var file = Path.Combine(wwwroot, "appsettings.json"); if (!File.Exists(file)) File.WriteAllText(file, "{}"); projectBuilder = projectBuilder.WithEnvironment(ctx => { //var loggerService = ctx.ServiceProvider.GetService(typeof(ResourceLoggerService)) as ResourceLoggerService; //var logger = loggerService?.GetLogger(testProject.Resource); if (!api.Resource.TryGetEndpoints(out var end)) return; if (!end.Any()) return; var fileContent = File.ReadAllText(file); Dictionary<string, object>? dict; if (string.IsNullOrWhiteSpace(fileContent)) dict = new Dictionary<string, object>(); else dict = JsonSerializer.Deserialize<Dictionary<string, object>>(fileContent!); ArgumentNullException.ThrowIfNull(dict); var val = end.FirstOrDefault()?.AllocatedEndpoint?.UriString ?? ""; if (!val.EndsWith("/")) val += "/"; if (dict.ContainsKey(nameOfParameter)) { // If the value is already set and matches, we can skip writing it again if (dict[nameOfParameter]?.ToString() == val) { ctx.Logger?.LogInformation($"Skipping writing {nameOfParameter} as it is already set to {val}"); return; } } dict[nameOfParameter] = val; JsonSerializerOptions opt = new JsonSerializerOptions(JsonSerializerOptions.Default) { WriteIndented = true }; File.WriteAllText(file, JsonSerializer.Serialize(dict, opt)); ctx.Logger?.LogInformation($"Successfully writing {nameOfParameter} as it is already set to {val}"); ctx.EnvironmentVariables[nameOfParameter] = val; }); return projectBuilder.WaitFor(api); }
To use it add this to a Aspire AppHost project
var apiService = builder.AddProject<Projects.BlazorExtensions_ApiService>("apiservice") .WithHttpHealthCheck("/health"); builder.AddWebAssemblyProject<Projects.BlazorWebAssProject>("webfrontend", apiService);
and then add this to a BlazorWebAssembly project
// name of the apiService in the AppHost project //var apiService = builder.AddProject<Projects.BlazorExtensions_ApiService>("apiservice") var hostApi = builder.Configuration["apiservice_host"]; if (string.IsNullOrEmpty(hostApi)) { hostApi = builder.HostEnvironment.BaseAddress; if (!hostApi.EndsWith("/")) { hostApi += "/"; } //uncomment the following lines to set a default value for the hostApi // var dict = new Dictionary<string, string?> { { "your name here", hostApi } }; // builder.Configuration.AddInMemoryCollection(dict.ToArray()); } builder.Services.AddKeyedScoped("api",(sp,_) => new HttpClient { BaseAddress = new Uri(hostApi) }); builder.Services.AddKeyedScoped("local_host", (sp, _) => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
and then inject
@page "/" <PageTitle>Home</PageTitle> <h1>API</h1> @httpClientAPI?.BaseAddress <h1>Local Host</h1> @httpClientLocal?.BaseAddress @code { [Inject(Key = "local_host")] public HttpClient? httpClientLocal { get; set; } [Inject(Key = "api")] public HttpClient? httpClientAPI { get; set; } }
Do not forget to add CORS to webAPI !