{"id":24499176,"url":"https://github.com/benjamincharlton/aspire4wasm","last_synced_at":"2025-04-14T05:32:47.852Z","repository":{"id":270650242,"uuid":"911028661","full_name":"BenjaminCharlton/Aspire4Wasm","owner":"BenjaminCharlton","description":"Allows you to add Aspire service discovery to Blazor WebAssembly (client) apps, storing service discovery information in appSettings.json or another means you choose.","archived":false,"fork":false,"pushed_at":"2025-01-19T05:27:35.000Z","size":936,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-19T06:25:06.320Z","etag":null,"topics":["aspire","blazor","blazor-client","blazor-webassembly","net-8","net-9","service-discovery","web-api"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/Aspire4Wasm/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BenjaminCharlton.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-02T04:46:56.000Z","updated_at":"2025-01-19T05:27:52.000Z","dependencies_parsed_at":null,"dependency_job_id":"9304e621-6ca2-4504-a43d-d5077ca627b8","html_url":"https://github.com/BenjaminCharlton/Aspire4Wasm","commit_stats":null,"previous_names":["benjamincharlton/aspire4wasm"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BenjaminCharlton%2FAspire4Wasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BenjaminCharlton%2FAspire4Wasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BenjaminCharlton%2FAspire4Wasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BenjaminCharlton%2FAspire4Wasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BenjaminCharlton","download_url":"https://codeload.github.com/BenjaminCharlton/Aspire4Wasm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235027942,"owners_count":18924624,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspire","blazor","blazor-client","blazor-webassembly","net-8","net-9","service-discovery","web-api"],"created_at":"2025-01-21T22:13:19.114Z","updated_at":"2025-01-21T22:13:19.882Z","avatar_url":"https://github.com/BenjaminCharlton.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Aspire4Wasm    \nAn easy way to pass service discovery information from a distributed application in Aspire down to your Blazor WebAssembly (client) applications. You can then add service discovery to the client app just like any other Aspire resource. Don't need the source code? Get the Nuget package: https://www.nuget.org/packages/Aspire4Wasm/\n\n## Problem statement\n.NET Aspire doesn't currently (as of early 2025) facilitate a Blazor WebAssembly (client) app discovering Aspire resources, even if the app has been added to the distributed application, because Blazor WebAssembly apps run in the browser and are \"standalone\". This has been commented on here:\n\n* https://github.com/dotnet/aspire/issues/4785\n\nThe expectation is that these apps will need to be aware of the web APIs they're supposed to call without relying on Aspire, and that they will store these in `appsettings.json` or `appsettings.{environmentName}.json`. This works fine, but if the endpoint changes, or if it differs in your development and production environments, you have to remember to manage those changes in your client app as well as your other resources. This is precisely the problem Aspire is intended to solve.\n\nMy little library Aspire4Wasm solves the problem by writing the service discovery information to the `appsettings.{environmentName}.json` file of your client app for you.\n\n## Quickstart\nInstall Aspire4Wasm in your AppHost project via the Nuget package. No need to install it on the client project.\nIn your AppHost project's Program.cs file:\n\n1. Add the Web Apis you want your client to be able to call.\n2. Add your Blazor Server app then chain a call to `AddWebAssemblyClient` to add your client app.\n5. Chain a call to `WithReference` to point the client to each web API (you can repeat this for as many Web APIs as you need)\n\nIn your client's `Program.cs` file:\n\n1. Call `AddServiceDiscovery`\n2. Configure your `HttpClient`s either globally or one at a time. In each client's `BaseAddress` property, use the name you gave to the resource in your AppHost.\n\nSee the example below:\n\n### Example Program.cs in AppHost\n```\nvar builder = DistributedApplication.CreateBuilder(args);\n\nvar inventoryApi = builder.AddProject\u003cProjects.AspNetCoreWebApi\u003e(\"inventoryapi\");\nvar billingApi = builder.AddProject\u003cProjects.SomeOtherWebApi\u003e(\"billingapi\");\n\nbuilder.AddProject\u003cProjects.Blazor\u003e(\"blazorServer\")\n    .AddWebAssemblyClient\u003cProjects.Blazor_Client\u003e(\"blazorWasmClient\")\n    .WithReference(inventoryApi)\n    .WithReference(billingApi);\n\nbuilder.Build().Run();\n```\n### Example Program.cs in your Blazor WebAssembly Client\nInstall (on the WebAssembly client) the `Microsoft.Extensions.ServiceDiscovery` Nuget package to get the official Aspire service discovery functionality that is going to read your resource information from your app settings.\n```\nbuilder.Services.AddServiceDiscovery();\nbuilder.Services.ConfigureHttpClientDefaults(static http =\u003e\n{\n    http.AddServiceDiscovery();\n});\n\nbuilder.Services.AddHttpClient\u003cIInventoryService, InventoryService\u003e(\n    client =\u003e\n    {\n        client.BaseAddress = new Uri(\"https+http://inventoryapi\");\n    });\n\n    builder.Services.AddHttpClient\u003cIBillingService, BillingService\u003e(\n    client =\u003e\n    {\n        client.BaseAddress = new Uri(\"https+http://billingapi\");\n    });\n```\n(I recommend extracting the names of the resources (e.g. \"billingapi\" and \"inventoryapi\") into a string constant somewhere shared by the AppHost and the client, and your other referenced projects. That way, the name is consistent throughout the whole solution.) You probably do this already though!\n## Default behaviour\nUsing the default behaviour (in the example) your AppHost will write the service discovery information for all the referenced resources into the `appsettings.{environmentName}.json` file of your client app for you.\nIt uses the following structure. The structure is important because it allows Aspire to \"discover\" the information on the client.\n```\n{\n  \"Services\": {\n    \"inventoryapi\": {\n      \"https\": [\n        \"https://localhost:1234\"\n      ],\n      \"http\": [\n        \"http://localhost:4321\"\n      ]\n    },\n    \"billingapi\": {\n      \"https\": [\n        \"https://localhost:9876\"\n      ],\n      \"http\": [\n        \"http://localhost:6789\"\n      ]\n    }\n  }\n}\n```\n## Custom behaviours (optional)\nIf you want to serialize the service discovery information some other way in your WebAssembly application (for example, in a different JSON file, or in an XML file) you can do so in the AppHost `Program.cs` by creating a custom implementation of `IServiceDiscoveryInfoSerializer` and passing it to the call to `AddWebAssemblyClient` via the `WebAssemblyProjectBuilderOptions` class, like this:\n```\nvar builder = DistributedApplication.CreateBuilder(args);\n\nvar inventoryApi = builder.AddProject\u003cProjects.AspNetCoreWebApi\u003e(\"inventoryapi\");\nvar billingApi = builder.AddProject\u003cProjects.SomeOtherWebApi\u003e(\"billingapi\");\n\nbuilder.AddProject\u003cProjects.Blazor\u003e(\"blazorServer\")\n    .AddWebAssemblyClient\u003cProjects.Blazor_Client\u003e(\"blazorWasmClient\" options =\u003e {\n        options.ServiceDiscoveryInfoSerializer = yourImplementation;\n    })\n    .WithReference(inventoryApi)\n    .WithReference(billingApi);\n\nbuilder.Build().Run();\n```\nIf you choose to make a custom implementation of `IServiceDiscoveryInfoSerializer`, you only need to override one method:\n```\npublic void SerializeServiceDiscoveryInfo(IResourceWithServiceDiscovery resource) { }\n```\nNote: If you choose to override the default behaviour with an output format that Aspire can't read from your WebAssembly client app, you'll also need to override the discovery behaviour on the client, which is outside the scope of what I've developed here.\n## Using service discovery to configure CORS in your web API (optional)\nYou can also reference one or more Blazor apps from a web API. One use case would be to configure Cross Origin Resource Sharing (CORS) in the web API to grant access to your clients to submit HTTP requests.\n### Example updated Program.cs in AppHost project\n```\nvar builder = DistributedApplication.CreateBuilder(args);\n\nvar blazorServer = builder.AddProject\u003cProjects.InMyCountry_UI_Server\u003e(\"blazorServer\"); // We'll call AddWebAssemblyClient a bit later this time, because we want to get this reference to the Blazor server project first.\n\nvar webApi = builder.AddProject\u003cProjects.InMyCountry_WebApi\u003e(\"inventoryApi\")\n .WithReference(blazorServer) // This will pass the endpoint URL of the Blazor app to the web API so that it can be added as a trusted origin in CORS.\n .WaitFor(blazorServer);\n\nblazorServer.AddWebAssemblyClient\u003cProjects.InMyCountry_UI_Client\u003e(\"blazorWasmClient\") // Now we can add the Blazor WebAssembly (client) app in the Aspire4Wasm package.\n    .WithReference(webApi); // And pass the Blazor client a reference to the web API\n\nbuilder.Build().Run();\n```\n### The example above will add the following to the appsettings{.Environment}.json file of the web API project\n```\n{\n  \"Clients\": [\n    \"https://{url of your blazor app should be here}\"\n  ]\n}\n\n```\nIt should add as many clients as you configured in the AppHost.\n### Example continued in Program.cs in the web API project\nNow that the web API has a reference to the Blazor app in appsettings, we can configure CORS like this:\n```\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.AddServiceDefaults();\n\nvar clients = builder.Configuration.GetSection(\"Clients\").Get\u003cstring[]\u003e() ?? []; // Get the clients from the list in appsettings.\n\nbuilder.Services.AddCors(options =\u003e\n{\n    options.AddDefaultPolicy(policy =\u003e\n    {\n        policy.WithOrigins(clients); // Add the clients as allowed origins for cross origin resource sharing.\n        policy.AllowAnyMethod();\n        policy.WithHeaders(\"X-Requested-With\");\n        policy.AllowCredentials();\n    });\n});\n\n// Etc.\n```\n## Troubleshooting\nThese are just a few things that I noticed helped me and I hope they help you too.\n* You don't need a `launchsettings.json` in your webassembly client project. The one in your Blazor server project will do.\n* In the `launchsettings.json` of your blazor server project, I recommend that you set `launchBrowser` to `false` for all profiles. This means that when the Aspire dashboard opens up, you'll need to click the link to open up your Blazor client. This is good! If you don't do this, your Blazor client is going to launch on a random port chosen by Aspire. When launched on a random port, your web API might reject the requests of your Blazor client because it doesn't have the expected origin to comply with the API's CORS policy. I tried to stop this happening but couldn't, so this is my workaround.\n## Contributing\nI'm a hobbyist. I know there are loads of people out there who be able to improve this in ways I can't, or see opportunities for improvement that I can't even imagine. If you want to contribute, bring it on! Send me a pull request.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenjamincharlton%2Faspire4wasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenjamincharlton%2Faspire4wasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenjamincharlton%2Faspire4wasm/lists"}