Aspire Blazor WebAssembly and WebAPI
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….