BlazorExtensionsAspire – integrate BlazorWebAssembly with WebAPI via Aspire–code

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 !


Posted

in

, , ,

by

Tags: