You need to enable JavaScript to run this app. .NET5 Blazor WASM HttpClientInterceptor调用GetCapturedContentAsync多次触发问题
优惠活动
大模型
产品
解决方案
定价
更多

.NET5 Blazor WASM HttpClientInterceptor调用GetCapturedContentAsync多次触发问题

问题根因

你遇到的重复触发拦截逻辑的问题由3个原因共同导致:

  • 你使用的HttpClientInterceptor库提供的GetCapturedContentAsync()方法内部会重新缓冲读取响应内容流,这个过程触发的内部HTTP上下文操作会被全局注册的拦截器再次捕获,重复执行错误处理逻辑。
  • 拦截逻辑末尾抛出的HttpRequestException会被上层HTTP调用方捕获,如果业务代码配置了重试策略,或是Blazor渲染线程的异步延续任务会再次触发请求处理流程。
  • 跳转错误页后没有终止当前拦截逻辑的执行,异步任务的后续操作会重复运行错误处理代码。

解决方案

按以下步骤修改代码即可解决问题:

  1. 新增请求处理标记,避免同一个请求被重复处理
  2. 直接读取e.Response.Content获取错误内容,不需要调用GetCapturedContentAsync()避免二次拦截
  3. 删除拦截逻辑末尾不必要的异常抛出,跳转错误页后直接终止逻辑执行
  4. 跳转后添加return语句,避免后续代码运行

修改后的完整代码

public class HttpInterceptorService
{
    private readonly HttpClientInterceptor _interceptor;
    private readonly NavigationManager _navManager;
    private readonly ILocalStorageService _localStorageService;

    public HttpInterceptorService(HttpClientInterceptor interceptor, NavigationManager navManager,
        ILocalStorageService localStorageService)
    {
        _interceptor = interceptor;
        _navManager = navManager;
        _localStorageService = localStorageService;
    }

    public void MonitorEvent() => _interceptor.AfterSendAsync += InterceptResponse;

    private async Task InterceptResponse(object sender, HttpClientInterceptorEventArgs e)
    {
        // 新增:已经处理过的请求直接跳过,避免重复触发
        if (e.Properties.ContainsKey("IsErrorHandled")) return;
        if (e?.Response == null || e.Response.IsSuccessStatusCode) return;
        // 标记当前请求已处理
        e.Properties.TryAdd("IsErrorHandled", true);

        await _localStorageService.RemoveItemAsync("apiError");

        var responseCode = e.Response.StatusCode;
        ApiError apiError;
        var message = "Something went wrong, please contact Administrator";
        string detail;

        switch (responseCode)
        {
            case HttpStatusCode.NotFound:
                message = "The requested resource was not found.";
                detail = "The resource was not found. Please report the error if the problem persists.";
                apiError = new ApiError {Title = message, Status = (short) responseCode, Detail = detail};
                await _localStorageService.SetItemAsync("apiError", apiError);
                _navManager.NavigateTo("/ErrorPage");
                return; // 新增:跳转后直接返回,终止后续逻辑
            case HttpStatusCode.Unauthorized:
            case HttpStatusCode.Forbidden:
                message = "You are not authorized to access this resource. ";
                detail = "Please contact Helpdesk and the can help you get access to the resource.";
                apiError = new ApiError {Title = message, Status = (short) responseCode, Detail = detail};
                await _localStorageService.SetItemAsync("apiError", apiError);
                _navManager.NavigateTo("/ErrorPage");
                return; // 新增:跳转后直接返回,终止后续逻辑
            case HttpStatusCode.InternalServerError:
                apiError = await GetApiError500FromContent(e) ?? new ApiError();
                if (string.IsNullOrEmpty(apiError.Id))
                {
                    apiError.Status = (short) responseCode;
                    apiError.Title = "Internal server error";
                }
                await _localStorageService.SetItemAsync($"apiError_{Guid.NewGuid()}", apiError);
                _navManager.NavigateTo("/ErrorPage");
                return; // 新增:跳转后直接返回,终止后续逻辑
            default:
                message = "Something went wrong, please contact Administrator";
                detail = "Please report the error if the problem persists.";
                apiError = new ApiError {Title = message, Status = (short) responseCode, Detail = detail};
                await _localStorageService.SetItemAsync($"apiError", apiError);
                _navManager.NavigateTo("/ErrorPage");
                return; // 新增:跳转后直接返回,终止后续逻辑
        }
    }

    private static async Task<ApiError> GetApiError500FromContent(HttpClientInterceptorEventArgs e)
    {
        try
        {
            // 修改:直接读取原始响应内容,不需要调用GetCapturedContentAsync
            var apiError = await e.Response.Content.ReadFromJsonAsync<ApiError>();
            if (apiError != null && !string.IsNullOrEmpty(apiError.Id))
            {
                return apiError;
            }
        }
        catch
        {
        }
        return null;
    }

    public void DisposeEvent() => _interceptor.AfterSendAsync -= InterceptResponse;
}

如果你的业务场景必须使用GetCapturedContentAsync(),可以在调用该方法前临时注销拦截器事件,调用完成后再重新订阅,不过上述方案的性能和稳定性更高。


内容的提问来源于stack exchange,提问作者Martin Andersen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.21 10:45:03