benjamin
benjamin

Reputation: 1644

HttpClient configured in Program.cs is not being passed to MediatR RequestHandler by dependency injection container

I'm working on a Blazor WebAssembly application in .NET 6.0.

I'm using MediatR requests and handlers.

public class DummyRequest : IRequest<string>
{
    public Guid Test { get; } = new Guid("e9f41a5d-5da6-4aad-b118-83476b7f40f4");
}


public class DummyHandler : IRequestHandler<DummyRequest, string>
{
    private readonly HttpClient _httpClient;

    public DummyHandler(HttpClient httpClient)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
    }

    public async Task<string> Handle(DummyRequest request, CancellationToken cancellationToken)
    {
      // This should be the value configured in Program.cs
        string baseAddress = _httpClient.BaseAddress?.AbsoluteUri ?? string.Empty;
        // But it's always blank, so we can't make any calls with the HttpClient

        await Task.CompletedTask;
        return "foobar";
    }
}

I'm configuring a different HttpClient for each request handler in Program.cs, then I'm adding MediatR:

builder.Services.AddHttpClient<DummyHandler>((client) => { client.BaseAddress = new Uri("https://api.somewhere.com"); });
builder.Services.AddMediatR(Assembly.GetExecutingAssembly());

I have also tried reversing those calls, so that I add MediatR first, and register the HttpClient for the DummyHandler type afterwards.

At runtime, after that Handler has been instantiated, it should have an _httpClient with a BaseAddress property set to "https://api.somewhere.com".

However, it always gets an HttpClient with a null BaseUri, so the Handler can't use the HttpClient in any operations.

Can anybody see what's gone wrong please?

Upvotes: 1

Views: 2067

Answers (4)

Yuriy Sountsov
Yuriy Sountsov

Reputation: 198

In case you are using the IRequestHandler<SomeCommand> alternative where there is no response, MediatR internally converts it to to IRequestHandler<SomeCommand, Unit>, which is what you will need to use to properly inject the HTTP client in your DI:

serviceCollection
    .AddHttpClient<IRequestHandler<SomeCommand, Unit>, SomeCommandHandler>((httpClient) =>
    {
        ...
    });

Upvotes: 0

JHBonarius
JHBonarius

Reputation: 11261

Instead of a typed httpclient, you could use a named httpclient.

Thus register as

builder.Services.AddHttpClient("somename", client => { client.BaseAddress = new Uri("https://api.somewhere.com"); });

And in the constructor, inject the httpclientfactory instead:

    public DummyHandler(HttpClientFactory httpClientFactory)
    {
        _httpClient = httpClientFactory.CreateClient("somename");
    }

Upvotes: 1

IKomarovLeonid
IKomarovLeonid

Reputation: 390

I suggest you to create the wrapper class around your Http client and register it instead.It hides implementation of your connection type and can be extended by other logic or other realization if you need.

Example:

class ApiConnection : IConnection
{
  private readonly HttpClient _client;

  public ApiConnection(...)
{
  _client = new HttpClient();
}
// some other logic

}

Add this class to your Handler (IConnection connection) and use it in handler.

Register as: services.AddSingleton<IConnection, APIConnection>();

Upvotes: 0

Guru Stron
Guru Stron

Reputation: 141690

It seems that MediatR registers interface-implemetation pair so you need to follow the same pattern for the typed client registration. Try the following:

services.AddHttpClient<IRequestHandler<DummyRequest, string>, DummyHandler>((client) => { client.BaseAddress = new Uri("https://api.somewhere.com"); });

Gist with full test code.

Upvotes: 2

Related Questions