5
votes

I'm using the basic template that VS 2019 provides with the weather forecasting data when creating a ASP.NET WebAPI project and added some very basic authentication with user login and support for JWT Token which all works fine.

I'm trying to create a blazor client project to consume the API and display the data on the page. AFAIK Blazor doesn't support localstorage so I'm using Blazored LocalStorage package to give me this ability. My problem stems from fact using JS via OnInitializedAsync() is not possible in server-side blazor (https://github.com/aspnet/AspNetCore/issues/13396) as a result I'm not sure how one is suppose to consume these web api calls. As this will produce a null reference exception

protected override async Task OnInitializedAsync()
{
    var client = HttpFactory.CreateClient();
    var token = await LocalStorage.GetItemAsync<string>("authToken");
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    var response = await client.GetAsync("url/WeatherForecast");
    var str = await response.Content.ReadAsStringAsync();
    Items = JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(str);
}

One suggestion was to use OnAfterRenderAsync() method to call them as JS would be ready by then. Which semi-works but obviously the UI doesn't match because it needs to be refreshed - however to manually refresh it seems I have to call StateHasChanged(); which in turn calls OnAfterRender method again and as a result I had to put a check but this ultimately feels incredibly hacky.

private bool hasRendered;
protected override async Task OnAfterRenderAsync(bool _)
{
    if (!hasRendered) return;
    var client = HttpFactory.CreateClient();
    var token = await LocalStorage.GetItemAsync<string>("authToken");
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    var response = await client.GetAsync("https://url/WeatherForecast");
    var str = await response.Content.ReadAsStringAsync();
    Items = JsonConvert.DeserializeObject<IEnumerable<WeatherForecast>>(str);

    StateHasChanged();

    hasRendered = true;
}

What is the correct way to consume an API with authnetication and display the data correctly on the client side?

Side question HttpClient doesn't seem to be injectable in server-side and it's recommended to use HttpClientFactory - is it a good idea to create a client on every request or make a singleton and re-use thoughout the client project?

1
You indicate you are creating a "blazor client project" but then later say "My problem stems from fact using JS via OnInitializedAsync() is not possible in server-side blazor"? Am I misunderstanding something? - Michael Washington
Apologises my client in this sense I meant like a UI sorta client. It's a server-sided blazor project. - A.A
If I am using server side Blazor there is no need to make any http calls :) Problem solved, right? - Michael Washington
What do you mean? How do you request API calls without http requests? - A.A
With server side Blazor you simply call directly into your server side controllers. It is totally secure. See; Creating A Step-By-Step End-To-End Database Server-Side Blazor Application blazorhelpwebsite.com/Blog/tabid/61/EntryId/4318/… - Michael Washington

1 Answers

1
votes

Q1

One suggestion was to use OnAfterRenderAsync() method to call them as JS would be ready by then. Which semi-works but obviously the UI doesn't match because it needs to be refreshed - however to manually refresh it seems I have to call StateHasChanged(); which in turn calls OnAfterRender method again and as a result I had to put a check but this ultimately feels incredibly hacky.

All people with the same issue, because this, at Lifecycle methods, new OnAfterRenderAsync with firstRender parm is documented:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ... /// your auth code here.
    }
}

Q2

Side question HttpClient doesn't seem to be injectable in server-side and it's recommended to use HttpClientFactory - is it a good idea to create a client on every request or make a singleton and re-use thoughout the client project?

Simplifying: I suggest to you to create two external libraries for your backend calls: one using http requests (for blazor wasm hosted model) and the other one just calling c# backend functions (for blazor server). Both with a common interface for backend calls. Use DI to set right library for each hosted model.