I have an ASP.NET Core Web API project as the backend and a Blazor web assembly as the front-end.
The Web API has ProductController
which returns JSON data (products, groups, etc). Blazor app consumes that API.
I created ProductService
on the Blazor side. The ProductService
gets HttpClient
in constructor injection and sends requests to the API. And here is the problem — I get a 404 NotFound error. When I use injected HttpClient
or create it directly on the page it works (please see the code shown below).
If I input API URL in a browser I get the data (product catalog).
I initialize HttpClient
as Microsoft says in the article.
Please help to fix this.
This is the Blazor init code:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
var appSettings = builder.Configuration.Get<AppSettings>();
builder.Services.AddSingleton<AppSettings>(appSettings);
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddHttpClient("Shop.WebApi", client => client.BaseAddress =
new Uri(appSettings.ApiBaseUri))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("Shop.WebApi"));
builder.Services.AddApiAuthorization();
builder.Services.AddScoped<CustomAuthenticationStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(
provider => provider.GetRequiredService<CustomAuthenticationStateProvider>());
builder.Services.AddScoped<AuthService>();
builder.Services.AddHttpClient<IProductService, HttpProductService>(client =>
{
client.BaseAddress = new Uri(appSettings.ApiBaseUri);
}).AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
await builder.Build().RunAsync();
This is product catalog page:
@inject IProductService ProductService
@inject HttpClient TheHttpClient
...
protected override async Task OnInitializedAsync()
{
try
{
// this works
_ProductCatalog = await TheHttpClient.GetFromJsonAsync<ProductCatalog>("api/Product/GetProductCatalog");
// this doesn't work
//await LoadProductCatalogAsync();
}
catch (Exception ex)
{
Console.WriteLine("An error occurred while initializing the page: " + ex.Message);
}
}
private async Task LoadProductCatalogAsync()
{
try
{
_ProductCatalog = await ProductService.GetProductCatalog();
}
catch (Exception ex)
{
Console.WriteLine("An error occurred while loading the product catalog: " + ex.Message);
}
}
Product service:
public class HttpProductService : IProductService
{
public HttpProductService(HttpClient httpClient)
{
TheHttpClient = httpClient;
}
public async Task<ProductCatalog> GetProductCatalog()
{
var response = await TheHttpClient.GetAsync($"{GetApiBaseUri()}");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadFromJsonAsync<ProductCatalog>();
}
return new ProductCatalog();
}
protected virtual string GetApiBaseUri() => $"api/Product";
protected HttpClient TheHttpClient { get; }
}
The problem in your code is the URL in the GetProductCatalog
was "api/Product" instead of "api/Product/GetProductCatalog".
Add the "/GetProductCatalog" path after GetApiBaseUri()
.
public class HttpProductService : IProductService
{
...
public async Task<ProductCatalog> GetProductCatalog()
{
var response = await TheHttpClient.GetAsync($"{GetApiBaseUri()}/GetProductCatalog");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadFromJsonAsync<ProductCatalog>();
}
return new ProductCatalog();
}
...
}