通过身份验证在 blazor 中使用 WebAPI

Consuming WebAPI in blazor with authentication

我在创建 ASP.NET WebAPI 项目时使用 VS 2019 提供的天气预报数据基本模板,并添加了一些非常基本的用户登录身份验证和对 JWT 令牌的支持一切正常。

我正在尝试创建一个 Blazor 客户端项目来使用 API 并在页面上显示数据。 AFAIK Blazor 不支持 localstorage 所以我使用 Blazored LocalStorage 包来给我这个能力。我的问题源于在服务器端 blazor (https://github.com/aspnet/AspNetCore/issues/13396) 中不可能通过 OnInitializedAsync() 使用 JS,因此我不确定应该如何使用这些 Web api 调用。因为这会产生空引用异常

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);
}

一个建议是使用 OnAfterRenderAsync() 方法调用它们,因为届时 JS 将准备就绪。哪个半工作但显然 UI 不匹配,因为它需要刷新 - 但是手动刷新似乎我必须调用 StateHasChanged();这又会再次调用 OnAfterRender 方法,结果我不得不进行检查,但这最终让人感觉难以置信。

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;
}

使用带身份验证的 API 并在客户端正确显示数据的正确方法是什么?

附带问题 HttpClient 似乎无法在服务器端注入,建议使用 HttpClientFactory - 在每个请求上创建一个客户端或创建一个单例并在整个客户端项目中重复使用是个好主意?

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.

所有遇到相同问题的人,因为在 Lifecycle methods,新的 OnAfterRenderAsyncfirstRender 参数被记录在案:

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?

简化:我建议您为后端调用创建两个外部库:一个使用 http 请求(用于 blazor wasm 托管模型),另一个仅调用 c# 后端函数(用于 blazor 服务器)。两者都具有用于后端调用的通用接口。使用 .