.NET5 Blazor WASM HttpClientInterceptor调用GetCapturedContentAsync多次触发问题
问题根因
你遇到的重复触发拦截逻辑的问题由3个原因共同导致:
- 你使用的
HttpClientInterceptor库提供的GetCapturedContentAsync()方法内部会重新缓冲读取响应内容流,这个过程触发的内部HTTP上下文操作会被全局注册的拦截器再次捕获,重复执行错误处理逻辑。 - 拦截逻辑末尾抛出的
HttpRequestException会被上层HTTP调用方捕获,如果业务代码配置了重试策略,或是Blazor渲染线程的异步延续任务会再次触发请求处理流程。 - 跳转错误页后没有终止当前拦截逻辑的执行,异步任务的后续操作会重复运行错误处理代码。
解决方案
按以下步骤修改代码即可解决问题:
- 新增请求处理标记,避免同一个请求被重复处理
- 直接读取
e.Response.Content获取错误内容,不需要调用GetCapturedContentAsync()避免二次拦截 - 删除拦截逻辑末尾不必要的异常抛出,跳转错误页后直接终止逻辑执行
- 跳转后添加
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
相关产品推荐
相关产品推荐

