dotnet / aspire

An opinionated, cloud ready stack for building observable, production ready, distributed applications in .NET
https://learn.microsoft.com/dotnet/aspire
MIT License
3.61k stars 400 forks source link

Is there any way to use ServiceDiscovery for client (aka browser) apps? #1536

Open paule96 opened 8 months ago

paule96 commented 8 months ago

Hi aspire team,

in my last try to use aspire I tried to add a blazor webassembly app to my project.

What I did so far is the following:

Add the blazor project to the aspire host with a reference to the backend:

using Aspire.Hosting;
var builder = DistributedApplication.CreateBuilder(args);

var server = builder.AddProject<Projects.ElsaServer>("elsaServer");
var webApp = builder.AddProject<Projects.ElsaStudio>("elsaStudio")
    .WithReference(server);

var app = builder.Build();
await app.RunAsync();

Now I added the required packages to get service discovery to my blazor project

    <PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0-preview.3.24052.7" />

Then I added in my Program.cs of my blazor project the following lines:

builder.Services.AddServiceDiscovery();
builder.Services.ConfigureHttpClientDefaults(http =>
{
    // Turn on resilience by default
    http.AddStandardResilienceHandler();

    // Turn on service discovery by default
    http.UseServiceDiscovery();
});

Then I use as BaseAddress for my httpClient http://elsaServer/elsa/api

But when I run my blazor application I always get a HTTP Request from the browser on http://elsaServer/elsa/api and not on http://localhost:43432/elsa/api.

Something seems to not work here. And I don't know how to fix it.

So what I would wish to do is something like:

builder.Services.AddServiceDiscovery("http://aspireHost/serviceDiscovery");

So you can specify in you client apps where aspire lives and provide an API that the clients can use to discover the services

DamianEdwards commented 7 months ago

The service discovery system requires configuration to have the service name mapped to the actual underlying endpoint address. For server apps composed into the Aspire AppHost project, this happens automatically by injecting environment variables into the processes when they start, that have names that follow the service discovery system's configuration-based provider (and because ASP.NET Core configuration defaults to reading from environment variables, it works).

For browser apps, there's no such automatic flowing of configuration for service discovery. You'll need to somehow flow the configuration details to the browser. If the HTML is initially server-rendered, you could emit the details there into the page in some format, e.g. data- attributes on the <body> tag, etc., and then author a custom implementation of IServiceDiscoveryEndPointResolver in your Blazor client app that reads from wherever you injected the data to. You could instead host an endpoint yourself that the implementation calls to from the browser to resolve addresses.

I might have a go at putting together a sample as this question has come up a few times and it would be good to have something to point at.

paule96 commented 7 months ago

@DamianEdwards maybe it would be nice to have a example how to host static content in aspire. Or what is the best way. I mean you can always deploy some kind of static website server but there are so many options out there that it could be a bit overwhelming to choose and also to build a good integration for it to aspire.

ReubenBond commented 7 months ago

Service Discovery does not support client-side apps today since, as @DamianEdwards said, there is no automatic flowing of configuration from the host to the client application. In the eShop sample, we use YARP to act as a proxy when the client needs to contact another service. In particular, loading images from the catalog: https://github.com/dotnet/eShop/blob/3b49f61a888656b038b5f044b0d4c5096fc9f073/src/WebApp/Program.cs#L29

For the MAUI frontend, we map each API which is needed by the app using a similar strategy here: https://github.com/dotnet/eShop/blob/3b49f61a888656b038b5f044b0d4c5096fc9f073/src/Mobile.Bff.Shopping/Program.cs#L12-L36

EDIT to clarify: in the eShop sample, instead of the clients communicating directly with backend API services, they only communicate directly with the service which the app is hosted on. That service proxies communication to the correct backend, using service discovery.

yarseyah commented 7 months ago

Is anything special needed to be done for the forwarder to make it work with Aspire's service discovery, e.g. I have added the following forwarder to Wasm Server:

app.MapForwarder("/api/weatherforecast", "http://apiservice", "/weatherforecast");

but I get the following error:

System.Net.Http.HttpRequestException: nodename nor servname provided, or not known (apiservice:80)

If I implement a new API in the server, and use HttpClient to manually make a call to the microservice, using the same http://apiservice it correctly communicates, so the service resolution works in the server, but not as part of the Yarp MapForwarder.

davidfowl commented 7 months ago

@yarseyah yes, add a reference to Microsoft.Extensions.ServiceDiscovery.Yarp, and call AddHttpForwarderWithServiceDiscovery.

1iveowl commented 1 month ago

@yarseyah yes, add a reference to Microsoft.Extensions.ServiceDiscovery.Yarp, and call AddHttpForwarderWithServiceDiscovery.

@davidfowl, as far as I can tell, adding a reference to Microsoft.Extensions.ServiceDiscovery.Yarp will not work for blazor webassembly clients, because Microsoft.Extensions.ServiceDiscovery.Yarp has a dependency to Microsoft.AspNetCore.App.

Trying to add Microsoft.Extensions.ServiceDiscovery.Yarp results in:

`Severity Code Description Project File Line Suppression State
Error (active) NETSDK1082 There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'. MyBlazor.Client C:\Program Files\dotnet\sdk\8.0.303\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets 491  

`

DamianEdwards commented 1 day ago

@1iveowl that package would be added to the server project in the case of an ASP.NET Core hosted Blazor WASM project, if using YARP to forward requests from the Blazor WASM app to another backend service.