Blazor Wasm HttpClientInterceptor Threading Issue

Viewed 382

Using latest NET5 Hosted Blazor WASM.

I am using the HttpClientInterceptor to catch errors returned from the server. All internal errors are catches and logged on the server and a error object are returned to the Blazor client.

I save the error object in local storage and then I navigate to the error page. The problem is that calling e.GetCapturedContentAsync() somehow triggers the HttpClientInterceptor class multiple times.
Picture below is one call to the server that returns a 500 with an error object. One of the entries holds data from the server the rest must be some kind of side effect of doing the async work.

enter image description here

When I am not calling e.GetCapturedContentAsync() then everthing works fine.

This is my HttpInterceptorService

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?.Response == null || e.Response.IsSuccessStatusCode) return;


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

        throw new HttpRequestException(message);
    }

    private static async Task<ApiError> GetApiError500FromContent(HttpClientInterceptorEventArgs e)
    {
        try
        {
            var capturedContent = await e.GetCapturedContentAsync();
            var apiError = await capturedContent.ReadFromJsonAsync<ApiError>();
            if (apiError != null && !string.IsNullOrEmpty(apiError.Id))
            {
                return apiError;
            }
        }
        catch
        {
        }

        return null;
    }

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

Simple answer (: I forgot to call dispose after each api call

Related