Aspnetcore: Replacement for WebAssemblyHttpMessageHandler?

Created on 15 Nov 2019  路  18Comments  路  Source: dotnet/aspnetcore

Describe the bug

In the latest release, .NET Core 3.1 Preview 3, WebAssemblyHttpMessageHandler was removed with no obvious replacement I could find for client-side Blazor.

I'm back to having "Call failed. Operation is not supported on this platform." exception when using Flurl. What is the correct HttpClientFactory for client-side Blazor?

To Reproduce

In Startup, ConfigureServices:

FlurlHttp.Configure(settings =>
{
    settings.HttpClientFactory = new DefaultHttpClientFactory();
});

Execute:

var response = await new Url(url).GetAsync();

var content = await response.Content.ReadAsStringAsync();

Direct usage of HttpClient works.

Previous workaround:

    public class HttpClientFactoryForBlazor : DefaultHttpClientFactory
    {
        public override HttpMessageHandler CreateMessageHandler()
        {
            return new WebAssemblyHttpMessageHandler();
        }
    }

// In Startup, ConfigureServices:
FlurlHttp.Configure(settings =>
{
    settings.HttpClientFactory = new HttpClientFactoryForBlazor();
});

I would still like to use Flurl because I have unit tests that verify that requests were generated correctly. I'm building a Cognitive Services API explorer, which can generate cURL as well as make a request to Cognitive Services.

For now, I'll revert back to Preview 2.

Further technical details

  • ASP.NET Core 3.1.0-preview3.19555.2
  • Include the output of dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   3.1.100-preview3-014645
 Commit:    b32d27f4b3

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18362
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\3.1.100-preview3-014645\

Host (useful for support):
  Version: 3.1.0-preview3.19553.2
  Commit:  13f35c3d86

.NET Core SDKs installed:
  2.1.201 [C:\Program Files\dotnet\sdk]
  2.1.202 [C:\Program Files\dotnet\sdk]
  2.1.400 [C:\Program Files\dotnet\sdk]
  2.1.401 [C:\Program Files\dotnet\sdk]
  2.1.402 [C:\Program Files\dotnet\sdk]
  2.1.403 [C:\Program Files\dotnet\sdk]
  2.1.500 [C:\Program Files\dotnet\sdk]
  2.1.502 [C:\Program Files\dotnet\sdk]
  2.1.503 [C:\Program Files\dotnet\sdk]
  2.1.504 [C:\Program Files\dotnet\sdk]
  2.1.505 [C:\Program Files\dotnet\sdk]
  2.1.507 [C:\Program Files\dotnet\sdk]
  2.1.508 [C:\Program Files\dotnet\sdk]
  2.1.600 [C:\Program Files\dotnet\sdk]
  2.1.601 [C:\Program Files\dotnet\sdk]
  2.1.602 [C:\Program Files\dotnet\sdk]
  2.1.604 [C:\Program Files\dotnet\sdk]
  2.1.700-preview-009597 [C:\Program Files\dotnet\sdk]
  2.1.700-preview-009601 [C:\Program Files\dotnet\sdk]
  2.1.700-preview-009618 [C:\Program Files\dotnet\sdk]
  2.1.700 [C:\Program Files\dotnet\sdk]
  2.1.701 [C:\Program Files\dotnet\sdk]
  2.1.800-preview-009677 [C:\Program Files\dotnet\sdk]
  2.1.800-preview-009696 [C:\Program Files\dotnet\sdk]
  2.1.800 [C:\Program Files\dotnet\sdk]
  2.1.801 [C:\Program Files\dotnet\sdk]
  2.2.100 [C:\Program Files\dotnet\sdk]
  2.2.204 [C:\Program Files\dotnet\sdk]
  2.2.300 [C:\Program Files\dotnet\sdk]
  2.2.301 [C:\Program Files\dotnet\sdk]
  2.2.400-preview-010195 [C:\Program Files\dotnet\sdk]
  2.2.400-preview-010219 [C:\Program Files\dotnet\sdk]
  2.2.400 [C:\Program Files\dotnet\sdk]
  2.2.401 [C:\Program Files\dotnet\sdk]
  3.0.100-preview7-012821 [C:\Program Files\dotnet\sdk]
  3.0.100-preview8-013656 [C:\Program Files\dotnet\sdk]
  3.0.100 [C:\Program Files\dotnet\sdk]
  3.1.100-preview1-014459 [C:\Program Files\dotnet\sdk]
  3.1.100-preview2-014569 [C:\Program Files\dotnet\sdk]
  3.1.100-preview3-014645 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.0-preview8.19405.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

  Microsoft.AspNetCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.0-preview3.19555.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]

  Microsoft.NETCore.App 2.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.3-servicing-26724-03 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.0-preview8-28405-07 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.0-preview3.19553.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.0.0-preview8-28405-07 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.0-preview3.19553.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download 
  • Visual Studio 2019 16.4 Preview 5
Done area-blazor blazor-wasm

Most helpful comment

I also encountered this issue and found a very hacky workaround based on code from here https://github.com/aspnet/AspNetCore/blob/271ebe019823e8e6a751429cd350c2db76ebab05/src/Components/Blazor/Blazor/src/Http/WebAssemblyHttpMessageHandlerOptions.cs:

c# var wasmHttpMessageHandlerType = Assembly.Load("WebAssembly.Net.Http").GetType("WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler"); var wasmHttpMessageHandler = (HttpMessageHandler)Activator.CreateInstance(wasmHttpMessageHandlerType); var httpClient = new HttpClient(wasmHttpMessageHandler);

All 18 comments

@jernejk thanks for contacting us.

@SteveSandersonMS Can you provide guidance here? I thought that as part of moving to netstandard2.1 we were using the out of the box httpclient.

I'm also encountering an issue with 3.1 Preview 3.

I'm using the Kubernetes C# Client in Blazor:

In Preview 2 the following worked:

services.AddTransient<WebAssemblyHttpMessageHandler>();

// Setup the http client
services.AddHttpClient("K8s")
   .ConfigurePrimaryHttpMessageHandler<WebAssemblyHttpMessageHandler>()
   .AddTypedClient<IKubernetes>((httpClient, serviceProvider) => new Kubernetes(serviceProvider.GetRequiredService<KubernetesClientConfiguration>(), httpClient));

In Preview 3, I changed the code to:

// Setup the http client
services.AddHttpClient("K8s")
   .AddTypedClient<IKubernetes>((httpClient, serviceProvider) => new Kubernetes(serviceProvider.GetRequiredService<KubernetesClientConfiguration>(), httpClient));

Now, the app throws the following exception:
Please note: Linker is disabled!

Unhandled exception rendering component:
System.NotImplementedException: Cannot invoke method because it was wiped. See stack trace for details.
  at System.Net.Http.HttpClientHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x387f300 + 0x00004> in <dff681a802cd429faf7e3556cf8262da>:0 
  at System.Net.Http.DelegatingHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x386ad60 + 0x00036> in <dff681a802cd429faf7e3556cf8262da>:0 
  at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.<>n__0 (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x387f1a8 + 0x0000e> in <f0b7b3feb5344f5fb506a82d63ee937f>:0 
  at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x387e7c0 + 0x0006e> in <f0b7b3feb5344f5fb506a82d63ee937f>:0 
  at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x386bd10 + 0x00148> in <f0b7b3feb5344f5fb506a82d63ee937f>:0 
  at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) <0x3891600 + 0x00264> in <dff681a802cd429faf7e3556cf8262da>:0 
  at k8s.Kubernetes.ListNamespaceWithHttpMessagesAsync (System.Nullable`1[T] allowWatchBookmarks, System.String continueParameter, System.String fieldSelector, System.String labelSelector, System.Nullable`1[T] limit, System.String resourceVersion, System.Nullable`1[T] timeoutSeconds, System.Nullable`1[T] watch, System.String pretty, System.Collections.Generic.Dictionary`2[TKey,TValue] customHeaders, System.Threading.CancellationToken cancellationToken) <0x38471a0 + 0x0079a> in <d1571f80f038409685da34e57cdd6943>:0 
  at k8s.KubernetesExtensions.ListNamespaceAsync (k8s.IKubernetes operations, System.Nullable`1[T] allowWatchBookmarks, System.String continueParameter, System.String fieldSelector, System.String labelSelector, System.Nullable`1[T] limit, System.String resourceVersion, System.Nullable`1[T] timeoutSeconds, System.Nullable`1[T] watch, System.String pretty, System.Threading.CancellationToken cancellationToken) <0x381d5f8 + 0x0012e> in <d1571f80f038409685da34e57cdd6943>:0 
  at UI.Components.Namespaces.OnInitializedAsync () [0x00058] in C:\Repos\UI\src\UI\Components\Namespaces.razor.cs:25 
  at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync () <0x3523428 + 0x0013e> in <44d78cab266c4647aa96aadd4455da2c>:0 

I also encountered this issue and found a very hacky workaround based on code from here https://github.com/aspnet/AspNetCore/blob/271ebe019823e8e6a751429cd350c2db76ebab05/src/Components/Blazor/Blazor/src/Http/WebAssemblyHttpMessageHandlerOptions.cs:

c# var wasmHttpMessageHandlerType = Assembly.Load("WebAssembly.Net.Http").GetType("WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler"); var wasmHttpMessageHandler = (HttpMessageHandler)Activator.CreateInstance(wasmHttpMessageHandlerType); var httpClient = new HttpClient(wasmHttpMessageHandler);

@campersau, your solution worked!

I've implemented the following:

// Setup the http client
services.AddHttpClient("K8s")
    .ConfigurePrimaryHttpMessageHandler( x=> 
    {
        var wasmHttpMessageHandlerType = Assembly.Load("WebAssembly.Net.Http").GetType("WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler");
        return (HttpMessageHandler)Activator.CreateInstance(wasmHttpMessageHandlerType);
    })
    .AddTypedClient<IKubernetes>((httpClient, serviceProvider) => new Kubernetes(serviceProvider.GetRequiredService<KubernetesClientConfiguration>(), httpClient));

An explanation of the reason for removing the WebAssemblyHttpMessageHandler class may probably be here: https://github.com/aspnet/AspNetCore/pull/16631#issuecomment-547397257
Is this correct?

The mono wasm HttpClientHandler does not support a lot of things and throws PlatformNotSupportedExceptions when setting some properties.
This might be the real issue for the OP here because Flurls DefaultHttpClientFactory always sets AutomaticDecompression without checking if SupportsAutomaticDecompression is true.

For the rest of us it looks like https://github.com/aspnet/Blazor/pull/1938 might fix our issues.

Yes, this is fixed with https://github.com/aspnet/Blazor/pull/1938, which will be included in the next release in December.

If you find there are parts of the API surface that don't work, but you believe they should work from a browser context (that is, it's not just a JavaScript limitation), then could you please report the details of that as a separate issue? Thanks!

Awesome!
Is this coming with Preview 4?

PS: Workaround from @campersau is working for me, which is to create an instance from WebAssembly.Net.Http assembly.

public class HttpClientFactoryForBlazor : DefaultHttpClientFactory
{
    public override HttpMessageHandler CreateMessageHandler()
    {
        var wasmHttpMessageHandlerType = Assembly.Load("WebAssembly.Net.Http").GetType("WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler");
        return (HttpMessageHandler)Activator.CreateInstance(wasmHttpMessageHandlerType);
    }
}

// In Startup, ConfigureService:
FlurlHttp.Configure(settings =>
{
    settings.HttpClientFactory = new HttpClientFactoryForBlazor();
});

@IvanJosipovic @jernejk
Now that preview 4 is out, do we still need to implement the following to configure an http client factory - or can it now be simplified?

 // Setup the http client
services.AddHttpClient("K8s")
    .ConfigurePrimaryHttpMessageHandler( x=> 
    {
        var wasmHttpMessageHandlerType = Assembly.Load("WebAssembly.Net.Http").GetType("WebAssembly.Net.Http.HttpClient.WasmHttpMessageHandler");
        return (HttpMessageHandler)Activator.CreateInstance(wasmHttpMessageHandlerType);
    })
    // etc

It still appears to be needed :(
image

Any news on this? Can't properly use HttpClientFactory right now, kinda sad.

I see it's fixed now with release of version 3.2.0 Preview 1 (Blazor WebAssembly)

And by the fix it means, services.AddHttpClient(); can be used.

I see it's fixed now with release of version 3.2.0 Preview 1 (Blazor WebAssembly)

Confirmed. Thanks!

Confirmed on my side as well when using Flurl and services.AddHttpClient();. :)

Although i'm still hitting the following issue - I still have to set the default base address myself like so:

 services.AddHttpClient(name, (sp, client) =>
            {
                var urlHelper = sp.GetRequiredService<NavigationManager>();
                client.BaseAddress = urlHelper.ToAbsoluteUri("");           
            });

Otherwise if I try to use the http client without doing this, and make requests to relative url's without setting a base address, it will error. Is this expected behaviour? I would have thought the sensible thing for blazor apps would be to have the base address set by default..

services.AddHttpClient() gives you an HttpClient with no base address. That's standard behavior for .NET and is by design.

If you want an HttpClient with a base address then you do have to configure that. We're making it easier to do so by providing a built-in servies.AddBaseAddressHttpClient method you can use: https://github.com/dotnet/aspnetcore/pull/19119

Was this page helpful?
0 / 5 - 0 ratings