Aspnetcore: Blazor Client - Consider a "reverse middleware pipeline" for blazor client side apps

Created on 21 Jun 2019  路  10Comments  路  Source: dotnet/aspnetcore

Is your feature request related to a problem? Please describe.

In my blazor client-side app, when I make an outgoing http request using HttpClient from some component, to some web api, and then receive a response back,
I find myself needing to extend what happens to the request before it gets sent over the wire, and likewise, intercept the response before it makes its way back into my component.

Update: see below comment's - the pipeline already exists in the form of HttpClient's DelegatingHandler's - but i'd like to see parity with asp.net core server side in being able to configure this pipeline with .UseXzy() extension methods in blazor client app, within startup.cs

For example, I want to append the current Authentication Header to all request that are sent - I don;t want each component to have to bother itself with that concern, instead I want all requests to flow through the same, configurable pipeline, managed as a separate concern, similar to asp.net core server side web apps today.

I want to intercept all responses so I can detect a particular response header indicating that the server has "pending upgrades" - so I can display a friendly message to the user, again I don't want each component to have to worry about that.

Lastly, and this is the key point really.. Rather than write my own wrapper around HttpClient that has all this prescriptive logic in for use within my app alone, I want to write re-usable "middleware" components that be added to anyones blazor client app, that sit in this processing pipeline and do things like add Jwt headers, or intercept responses and do things. I'd like for them to be able to add my "middleware" components to their app using a similar / standardised convention, like how on on the server side the convention is app.UseXyz().

Describe alternatives you've considered

I've written my own rudimentary pipeline within my app, and I have the concept of such middlewares - but as this is not formally part of the framework it doesn't benefit the blazor ecosystem as a whole, or allow me to share my "middleware" components with others.

area-blazor question

All 10 comments

You can use DelegatingHandlers instead of wrapping the HttpClient. Make sure that the inner most handler is a WebAssemblyHttpMessageHandler.

Here is a good article about that: https://thomaslevesque.com/2016/12/08/fun-with-the-httpclient-pipeline/

@campersau thanks - that looks perfect for defining a middleware pipeline on the client. I think what is missing from a blazor client perspective then is just a prescribed way to configure this "pipeline" of delegating handlers for the HttpClient that is registered by default. Do you have any example of that?

I'd love to see Blazor client have some IAppBuilder concept so you could configure this in startup.cs - with .UseXzy() extensions methods that basically just build that pipeline for the default http client via DelegatingHandler's

You can try out Microsoft.Extensions.Http https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.0
Make sure to use ConfigurePrimaryHttpMessageHandler and return the WebAssemblyHttpMessageHandler.

@campersau
Thanks I'll give it a go. I'm a little hesitant as I've not used IHttpClientFactory before it looks like its highly extensible, I don't want to waste too much time trying to figure out how best to configure it for blazor. Some options like typedclients look like they could cause perf issues with blazor - as they get registered as transients. If you use the option it would result in a new transient being injected into every component (all wrapping the same underlying http client) which seems like it would be creating needless object instances? A sample or gist showing correct setup and usage of IHttpClientFactory in a blazor app that is verified for performance would be immensely valuable. Showing a single configured pipeline (default HttpClient) and multiple independently configured pipelines (i.e IHttpClientFactory) would bother be useful.

I felt that Microsoft.Extensions.Http IHttpClientFactory was somewhat complicating things, as all I wanted to do is apply some configuration to the default HttpClient instance that blazor sets up.

This is the method I have so far - as you can see it's a bit cumbersome and not using any fluent / builder style registration api for building the pipeline (which is what I'd like to see in blazor):

            // Register each delegating handler as transient.. not sure if they could get away with being singletons.
            services.AddTransient<WebAssemblyHttpMessageHandler>();
            services.AddTransient<JwtAccessTokenHeaderHandler>();
            services.AddTransient<CsrfHeaderHandler>();

            services.AddSingleton<HttpClient>(sp =>
            {
               // Resolve each delegating handler and construct the pipeline manually. We do this so the delegating handlers can have dependencies injected, they often need things like `ITokenManager`'s etc etc.
                var jwtAccessTokenHeaderHandler = sp.GetRequiredService<JwtAccessTokenHeaderHandler>();
                var csrfHeaderHandler = sp.GetRequiredService<CsrfHeaderHandler>();
                var webAssyHandler = sp.GetRequiredService<WebAssemblyHttpMessageHandler>();
                jwtAccessTokenHeaderHandler.InnerHandler = csrfHeaderHandler;
                csrfHeaderHandler.InnerHandler = webAssyHandler;

               // Todo: figure out how to do the above using .UseXyz() extension method style.
                var client = new HttpClient(jwtAccessTokenHeaderHandler);
                var uriHelper = sp.GetRequiredService<IUriHelper>();              
                client.BaseAddress = new System.Uri(uriHelper.GetBaseUri());
                return client;
            });

Now the HttpClient that is injected throughout the blazor app has the expected pipeline. If I need additional configurations of HttpClient, then I think it might make sense to look at https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.0 but again - that will suffer from a lack of a formal middleware registration API.

@campersau
Also I kind of fail to see where the value is in IHttpClientFactory when I can already register my own type, that takes a http client in its constructor, and configure that http client directly in the factory method registered with DI like:

services.AddSingleton<MyClient>((sp)=> new MyClient(ConfigureHttpClient(sp)));

This let's me configure the httpclient for my typed service AND let's that typed service be registered as a singleton or scoped (not forced into transient which may result in unnecessary instances being created), AND means none of my code base has to take a dependeny on IHttpClientFactory..

Am I missing something that makes IHttpClientFactory more attractive than this?

Thanks for contacting us, @dazinator.
HttpClientFactory has a ton of benefits and covers many aspects simplifying the way the pipeline is configured. You can read more about it at https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.2

As for the rest, we plan to revisit the HttpClient usage in Blazor as part of https://github.com/aspnet/aspnetcore/issues/10397

Thank you. I'm loving blazor so far and I look forward to this being more fully fleshed out in a future release!

After reading up a lot more, it turns out IHttpClientFactory did a lot more than I thought, and having a singleton HttpClient isn't the greatest solution due to it not picking up dns changes.

I've changed my code to use IHttpClientFactory and it's working well. Thanks @campersau for the suggestion.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

moodya picture moodya  路  153Comments

natemcmaster picture natemcmaster  路  213Comments

rmarinho picture rmarinho  路  78Comments

danroth27 picture danroth27  路  130Comments

barrytang picture barrytang  路  89Comments