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 !