Blazor WASM - 全局捕获 401 并导航到所有 HttpClient 调用的登录页面

Łuk*_*wik 6 c# blazor blazor-webassembly

在中blazor template httpclient添加Program.cs class

builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
Run Code Online (Sandbox Code Playgroud)

后来使用如下:

forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
Run Code Online (Sandbox Code Playgroud)

有什么方法可以覆盖该基本 httpclient 并将其注入到我的所有页面中,这将:

  • 捕获任何服务器请求(post/get 等)中返回的 401
  • 尝试刷新令牌(为此调用API)
  • 刷新失败返回登录

我发现我可以定义一个自定义服务来包装所有这些 HTTP 客户端调用并执行我提到的操作,但是有没有更好的方法来做到这一点?

mil*_*Way 8

  1. 创建自定义DelegatingHandler处理 401 响应代码。
public class CustomMessageHandler : DelegatingHandler
{
    private readonly string host;
    readonly NavigationManager _navigationManager;

    CustomMessageHandler(IWebAssemblyHostEnvironment webAssemblyHostEnvironment, navigationManager)
    {
        host = webAssemblyHostEnvironment.BaseAddress;
        _navigationManager = navigationManager;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);

        if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
        {
            Console.WriteLine("CustomMessageHandler catch 401");

            // TODO: some logic (e.g. refreshing token)
            
            // Or just redirect to login page.
            string loingUrl = "http://some-site/login";
            _navigationManager.NavigateTo(loingUrl, forceLoad: true);
        }

        return response;
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. IServiceCollection中注册创建的CustomMessageHandler(在blazor.Client项目中)
builder.Services.AddScoped<CustomMessageHandler>();
Run Code Online (Sandbox Code Playgroud)
  1. 还要使用我们的 CustomMessageHandler 注册配置的 httpClient(在 blazor.Client 项目中)。 .AddHttpClient需要Microsoft.Extensions.Httpnuget 包。
builder.Services.AddHttpClient("BlazorServerHttpClient", 
    client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
        .AddHttpMessageHandler<CustomMessageHandler>();
Run Code Online (Sandbox Code Playgroud)
  1. 所有从 blazor.Client 到 blazor.Server 的 httpClient 请求都必须使用 IHttpClientFactory,如下所示:
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;

public class WeatherService
{
    private readonly IHttpClientFactory _clientFactory;

    public WeatherService(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task<WeatherForecast[]> GetWeatherForecast()
    {
        var client = _clientFactory.CreateClient("BlazorServerHttpClient");
        WeatherForecast[] result = await client.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");

        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这工作正常,只是在浏览器重定向之前 Blazor 会显示错误栏并且控制台记录“未处理的异常渲染组件:响应状态代码不指示成功:401(未经授权)”。 (2认同)

Pet*_*ris 2

如果您想通过 HttpClient 拦截所有调用,那么您需要创建一个允许您进行 HttpCalls 的服务,以便您可以在其中拦截它们并使用 HttpClient。

PS:对于任何实现 IDisposable 的类(例如 HttpClient),您应该将注册从 Transient 更改为 Scoped。这将在未来的 Blazor 模板中发生改变。