Blazored / Toast

A JavaScript free toast library for Blazor and Razor Component applications
https://blazored.github.io/Toast/
MIT License
663 stars 92 forks source link

[Bug] Toast not displayed when called from HttpMessageHandler #93

Closed stevieTheCoder closed 3 years ago

stevieTheCoder commented 4 years ago

Describe the bug Toast is not displayed when called from an HttpMessageHandler.

To Reproduce Register a Scoped handler implementing DelegatingHandler, inject IToastService into the handler. Call toast.ShowError from the handler.

Expected behavior Toast message is displayed

Hosting Model (is this issue happening with a certain hosting model?):

Additional context

builder.Services.AddScoped<ApiErrorHandler>();

builder.Services.AddHttpClient("API", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
                .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>()
                .AddHttpMessageHandler<ApiErrorHandler>();   

builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("API"));

Api Handler class

private readonly ILogger _logger;
        private readonly IToastService toastService;

        public ApiErrorHandler(ILogger<ApiErrorHandler> logger, IToastService toastService)
        {
            _logger = logger;
            this.toastService = toastService;
        }

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

                // Pass through
                if (response.IsSuccessStatusCode)
                {
                    return response;
                }                

                if (response.StatusCode == HttpStatusCode.BadRequest)
                {
                    var result = await JsonSerializer.DeserializeAsync<ProblemDetail>(await response.Content.ReadAsStreamAsync(), new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });

                    toastService.ShowError(result.Detail, result.Title);
                }

                return response;
            }
            catch (Exception ex)
            {
                _logger.LogError($"Failed to get response: {ex}");
                throw;
            }
        }
stevieTheCoder commented 4 years ago

As an interim work around registering IToastService as a singleton resolves the issue. Is there another solution?

chrissainty commented 3 years ago

Hi @stevieTheCoder, I'll have to investigate this one. I'm not sure why changing the IToastService registration to a singleton has fixed the issue. Usually, this points to the IToastService being injected into a service with a longer lifetime than its own. However, all your code appears to have a scoped lifetime, which is the same as the IToastService.

stevieTheCoder commented 3 years ago

Hi Chris I believe this is the reason why. https://www.google.com/amp/s/andrewlock.net/understanding-scopes-with-ihttpclientfactory-message-handlers/amp/ Unfortunately I don't think you can use the httpcontextaccessor in blazor wasm to resolve the dependencies as described.

chrissainty commented 3 years ago

Thanks for the info @stevieTheCoder. This seems a general oddity of ASP.NET Core and not an issue with this library. I don't think there is anything I can do, so I'm going to close this issue.