Aspire is the new visualizer – see https://github.com/dotnet/aspire
I am very fond of WebAPI – it allows for all people to see the functionality of a site , in a programmatic way ( side note: , my nuget package, https://www.nuget.org/packages/NetCore2Blockly , allows to make workflows from your WebAPI)
And Blazor WebAssembly is a nice addition that the WebAPI . I am talking about Interactive WebAssembly (https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?preserve-view=true&view=aspnetcore-8.0 ) . I do want ( for the moment ) to use Interactive Server because
- it is easy to forget to add functionality to the WebAPI
- it is not separating UI from BL
So I decided to add an Blazor WebAssembly and WebAPI into Aspire to see how they work together.
The first problem that I have is how to transmit the WebAPI URL to the Blazor WebAssembly . Think that is not Interactive Server or Auto – in order to have the environment or configuration . Blazor Interactive WebAssembly are just static files that are downloaded to the client. And they are executed in the browser.
But I have tried with adding to the Environment in usual way
builder.AddProject<projects.exampleblazorapp>(nameof(Projects.ExampleBlazorApp))
.WithEnvironment(ctx =>
{
if (api.Resource.TryGetAllocatedEndPoints(out var end))
{
if (end.Any())
ctx.EnvironmentVariables["HOSTAPI"] = end.First().UriString;
}
And no use!
After reading ASP.NET Core Blazor configuration | Microsoft Learn and aspire/src/Microsoft.Extensions.ServiceDiscovery at main · dotnet/aspire (github.com) and API review for Service Discovery · Issue #789 · dotnet/aspire (github.com) I realized that the ONLY way is to put in wwwroot/appsettings.json
So I came with the following code that tries to write DIRECTLY to wwwroot/appsettings.json file
namespace Aspire.Hosting;
public static class BlazorWebAssemblyProjectExtensions
{
public static IResourceBuilder<ProjectResource> AddWebAssemblyProject<TProject>(
this IDistributedApplicationBuilder builder, string name,
IResourceBuilder<ProjectResource> api)
where TProject : IServiceMetadata, new()
{
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 =>
{
if (api.Resource.TryGetAllocatedEndPoints(out var end))
{
if (end.Any())
{
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);
dict["HOSTAPI"] = end.First().UriString;
JsonSerializerOptions opt = new JsonSerializerOptions(JsonSerializerOptions.Default)
{ WriteIndented=true};
File.WriteAllText(file,JsonSerializer.Serialize(dict,opt));
ctx.EnvironmentVariables["HOSTAPI"]=end.First().UriString;
}
}
});
return projectbuilder;
}
}
And in Aspire
var api = builder.AddProject<Projects.ExampleWebAPI>(nameof(Projects.ExampleWebAPI));
builder.AddWebAssemblyProject<Projects.ExampleBlazorApp>(nameof(Projects.ExampleBlazorApp), api);
And in Blazor Interactive WebAssembly
var hostApi = builder.Configuration["HOSTAPI"];
if (string.IsNullOrEmpty(hostApi))
{
hostApi = builder.HostEnvironment.BaseAddress;
var dict = new Dictionary<string, string?> { { "HOSTAPI", hostApi } };
builder.Configuration.AddInMemoryCollection(dict.ToArray());
}
builder.Services.AddKeyedScoped("db",(sp,_) => new HttpClient { BaseAddress = new Uri(hostApi) });
What about deploying the code to production ? Well, I think that is better to wrote yourself to wwwroot/appsettings.json and remove the data . But I will try to deploy and let you know….