Typed client using HttpClient subclass - c#

In my app I will have few TypedClient services.
However, these classes will share a bunch of methods.
My solution to this is to create CustomHttpClient:
public class CustomHttpClient:HttpClient
{
//here shared methods
}
Then my typed client classes will use this derived class instead of standard HttpClient:
public class MyService : IMyService
{
public SomeService(CustomHttpClient client, IConfiguration configuration){}
}
However, if i try to register this service in startup i get an error that there is no suitable constructor for 'MyService' :
services.AddHttpClient<IMyService, MyService>();
In the documentation I have found:
A Typed Client is a class that accepts an HttpClient object (injected through its constructor) and uses it to call some remote HTTP service
Does it mean, that it cannot accept a subclass of HttpClient?
If this is the case, then my only solution would be to implement shared methods as HttpClient extension methods ( i don't really like this solution).
Is there maybe a workaround this, or extension methods are my only way out?
I have tried registering also CustomHttpClient so DI container would find it but the error is still the same.
What can you advise me?

Does it mean, that it cannot accept a subclass of HttpClient?
Yes.
If this is the case, then my only solution would be to implement shared methods as HttpClient extension methods
No. Per the docs typed clients encapsulate an HttpClient, rather than extending it. You configure the HttpClient in the constructor then add custom methods to the Typed Client that use the encapsulated HttpClient instance.
If you don't want to use the framework's pattern for HttpClient handling, you're free to create your own, but it's probably not worth the effort.
You can have typed clients that share a base class. eg
public class MyBaseTypedClient
{
public HttpClient Client { get; }
public MyBaseTypedClient(HttpClient client)
{
client.BaseAddress = new Uri("https://api.github.com/");
// GitHub API versioning
client.DefaultRequestHeaders.Add("Accept",
"application/vnd.github.v3+json");
// GitHub requires a user-agent
client.DefaultRequestHeaders.Add("User-Agent",
"HttpClientFactory-Sample");
Client = client;
}
//other methods
}
public class MyTypedClient : MyBaseTypedClient
{
public MyTypedClient(HttpClient client) : base(client) { }
}

If you need to add common methods only you can create an Interface with default implementation of those methods, then you would just need to inherit your IMyService with that interface.
You can also have a look at the below link which has some interesting workarounds.
https://github.com/dotnet/extensions/issues/1988

I think this is what you want to do:
(1) Create your base MyBaseTypedClient as follows:
public interface IMyBaseTypedClient
{
//other methods
//like FetchAsync(), PostAsync()
}
public class MyBaseTypedClient
{
private HttpClient _client
public MyBaseTypedClient(HttpClient client)
{
_client = client;
}
//other methods
//like FetchAsync(), PostAsync()
}
(2) Then create your typed clients as follows:
public interface IServiceOne: IMyBaseTypedClient{ }
public class ServiceOne: MyBaseTypedClient, IServiceOne
{
public ServiceOne(HttpClient httpClient) : base(httpClient)
{
}
}
public interface IServiceTwo: IMyBaseTypedClient{ }
public class ServiceTwo: MyBaseTypedClient, IServiceTwo
{
public ServiceTwo(HttpClient httpClient) : base(httpClient)
{
}
}
(3) Then register as follows
services
.AddHttpClient<IServiceOne, ServiceOne>(c => c.BaseAddress = new Uri("https://serviceone.com"));
.AddHttpClient<IServiceTwo, ServiceTwo>(c => c.BaseAddress = new Uri("https://servicetwo.com");
(4) Then inject as follows
public void SomeMethodThatNeedsOne(IServiceOne serviceOne)
{
//etc
}
public void SomeMethodThatNeedsTwo(IServiceTwo serviceTwo)
{
//etc
}

Related

Is it viable/thread-safe to use a static HttpClient in a using block?

Say I have a controller with an Index() method, and this controller utilizes multiple "Manager classes" that manage certain assets that need to be retrieved with an HttpClient from an API.
I've read that sharing an HttpClient with multiple calls is better than to reinstantiate it with every call to save ports.
I do however want to dispose of the HttpClient before the controller returns the view, because the view contains an entire Knockout/Typescript based front end project that handles the rest of the data (so it's basically only settings and meta data stuff).
Do I need to pass the HttpClient variable to each and every "Manager class", or does it suffice to do something like the following, and use a static HttpClient inside the classes?
public ActionResult Index()
{
using (Globals.Client = new System.Net.Http.HttpClient())
{
// do stuff like SettingManager.GetSetting("settingKey") which uses
// the Globals.Client variable
}
return View();
}
Or should I not even want to dispose the HttpClient in the first place?
One solution is to make a separate dependency responsible for managing your HttpClient. This has the side benefit of keeping your controllers from depending directly on HttpClient. Any class that depends on HttpClient becomes harder to test. It's also a maintenance issue because if you want to change the behavior you have to change it everywhere. Imagine if you decide one day that whatever you're getting from that HttpClient can be cached? You'd have to change it in lots of classes.
You can define an abstraction and implementation like this:
public interface IDoesSomething
{
string GetSetting(string key);
}
public class HttpClientDoesSomething : IDoesSomething, IDisposable
{
private readonly HttpClient _client;
private readonly string _apiUrl;
public HttpClientDoesSomething(string apiUrl)
{
_client = new HttpClient();
_apiUrl = apiUrl;
}
public string GetSetting(string key)
{
// use the client to retrieve the setting
}
public void Dispose()
{
_client?.Dispose();
}
}
Now the problem is moved out of your controller because you inject the interface:
public class MyController : Controller
{
private readonly IDoesSomething _doesSomething;
public MyController(IDoesSomething doesSomething)
{
_doesSomething = doesSomething;
}
public ActionResult Index()
{
var setting = _doesSomething.GetSetting("whatever");
// whatever else this does.
return View();
}
}
Now in your startup configuration you can register HttpClientDoesSomething as a singleton:
services.AddSingleton<IDoesSomething>(new HttpClientDoesSomething("url from settings"));
Your implementation is disposable, so if you do need to create and dispose it you will also dispose the HttpClient. But it won't be an issue because your application will keep reusing the same one.

BaseController for HttpClient Instance

I have some API client to make request. Those are described in startup.
Simply, is it make sense to create HttpClient via base class and calling common request methods from base. Or each controller should create own client ? Is there will be a problem ?
Base
public class BaseController : ControllerBase
{
public HttpClient client;
public BaseController(IHttpClientFactory factory, string clientName)
{
client = factory.CreateClient(clientName);
}
public async Task<IActionResult> Get(string query)
{
}
}
Foo
public class FooController : BaseController
{
public FooController(IHttpClientFactory factory) : base(factory, "fooclient")
{
}
[HttpGet]
public async Task<IActionResult> Get(int id)
{
return await Get($"Foo/Get/{id}");
}
}
There's nothing technically wrong with this approach, but it's preferable to use typed clients. The way that is done is by creating a "service" class which will own the client:
public class FooService
{
private readonly HttpClient _httpClient;
public FooService(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
...
}
Then, you register this in ConfigureServices:
services.AddHttpClient<FooService>(c =>
{
// configure your HttpClient
});
Finally, you inject this service class into your controller:
public class FooController : ControllerBase
{
private readonly FooService _fooService;
public FooController(FooService fooService)
{
_fooService = fooService ?? throw new ArgumentNullException(nameof(fooService));
}
...
}
This then serves to encapsulate your HttpClient logic. You simply add methods to the service to do things the controller needs and the service makes the actual HttpClient requests to do that. That makes it infinitely easier to change things if the API you're utilizing should change. You just change the service and you're good to go, instead of having to track down every place you used HttpClient to interact with this API, which is a much more difficult task.
Additionally, having the client be typed gives you the ability to configure it once for all, as well as add things like retry and exception handling policies in one place. Since the client is injected for a particular type (i.e. FooService) there's no magic strings for the client name that you could fat finger or otherwise mess up.

Need suggestions in help making a conditional parameter initialize a class with DI, C#, and Web API 2.0

I am using Unity dependency injection and Web API 2.0.
I have a controller which i am injecting my unit of work:
public ProductController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
I then have a method in that controller:
public HttpResponseMessage UpdateProducts(string website)
{
ProductAPI productAPI;
Enum.TryParse(website, out Website website);
productAPI = new ProductAPI(_unitOfWork, website);
productAPI.UpdateProducts();
}
The method takes in a parameter website which is a enum. It has 3 different values (website1, website2, website3).
This is the ProductAPI constructor that takes in UOW and the website enum:
public ProductAPI(IUnitOfWork unitOfWork, Website website)
{
_unitOfWork = unitOfWork;
_website = website;
_httpClient = CommonAPI.GetHttpClient(website);
}
The CommonAPI class is a static class that returns the httpclient based on the website.
public static HttpClient GetHttpClient(Website website)
{
HttpClient httpClient = new HttpClient();
if (website == Website.1)
{
//return httpclient for website 1
}
else if (website == Website.2)
{
//return httpclient for website 2
}
else if (website == Website.3)
{
//return httpclient for website 3
}
return httpClient;
}
The ProductAPI class also has calls within its methods that use the UOW but also use the website enum to filter based on the website value:
List<ProductViewModel> products = _unitOfWork.ProductRepository.GetProducts(_website.ToString());
Is there a better and cleaner way to set this code up based on that website enum?
With your static method, basically you are using the factory method pattern. It's a common pattern, but if you want more flexibility you could use an abstract factory as described here.
The caller is defining what kind of website is used to update the products So, placing that logic in the controller makes perfect sense.
In general; there could be some higher level of abstraction but the code seems fine just as it is.
But if you want to get your hands dirty: you could abstract the WebClient.
E.g.:
Define a common interface:
public interface IYourTypicalHandler{ /*def here */}
And implement it:
public class Web1Client : IYourTypicalHandler, HttpClient { /*logic here */ }
public class Web2Client : IYourTypicalHandler, HttpClient { /*logic here */ }
public class Web3Client : IYourTypicalHandler, HttpClient { /*logic here */ }
Now you have encapsulated and isolated your specific web behavior in different classes. This can be used as implementation strategy for your ProductAPI.
First create an interface for it:
public interface IProductApi { /* def here */ }
The signature of the implementation will be:
public class ProductAPI : IProductApi {
public ProductAPI(IUnitOfWork unitOfWork, IYourTypicalHandler)
}
Now you can use the IYourTypicalHandler, to invoke the actual call. These modifications split up implementation details and makes the code easier to unit-test, and most likely, easier to use in a DI driven environment.
Of course you need to rewire your factory:
//note: not bound to http anymore ;-)
public static IYourTypicalHandler GetHttpClient(Website website)
At this point you don't have access to _website.ToString() in:
var products = _unitOfWork.ProductRepository.GetProducts(_website.ToString());
which might complicate things, and might be targeted as well. So, it's up to you if it's worth it ;-)
Hope this helps.

Unable to resolve service for type 'System.Net.Http.HttpClient'

I created a ViewComponent class which call a REST API using the HttpClient, this is the code:
public class ProductsViewComponent : ViewComponent
{
private readonly HttpClient _client;
public ProductsViewComponent(HttpClient client)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
}
public async Task<IViewComponentResult> InvokeAsync(string date)
{
using(var response = await _client.GetAsync($"/product/get_products/{date}"))
{
response.EnsureSuccessStatusCode();
var products = await response.Content.ReadAsAsync<List<Products>>();
return View(products);
}
}
}
I get this error:
InvalidOperationException: Unable to resolve service for type 'System.Net.Http.HttpClient' while attempting to activate MyApp.ViewComponents.ProductsViewComponent'
I injected the HttpClient in the ConfigureService method available in Startup in this way:
services.AddHttpClient<FixturesViewComponent>(options =>
{
options.BaseAddress = new Uri("http://80.350.485.118/api/v2");
});
UPDATE:
I registered the ProductsViewComponent too, same error.
I had a similar problem - the problem was in double registration:
services.AddHttpClient<Service>();
services.AddSingleton<Service>(); // fixed by removing this line
Similar examples [just adding to clarify that it's not specific to AddSingleton, nor related to the order.]
services.AddScoped<IService, Service>(); // fixed by removing this line
services.AddHttpClient<IService, Service>();
TLDR;
ViewComponents do not support typed clients out of the box. To resolve this, add a call to AddViewComponentsAsServices() onto the end of the call to services.AddMvc(...).
After a pretty long chat that ran off the back of being able to reproduce your issue, we determined initially that the problem being observed is specific to ViewComponents. Even with a call to IServiceCollection.AddHttpClient<SomeViewComponent>(), passing an instance of HttpClient into SomeViewComponents constructor just refused to work.
However, sitting a new class (SomeService) between SomeComponent and HttpClient works as expected. This is what the docs refer to as a typed client. The code looks a bit like this:
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeService>();
// ...
}
// SomeService.cs
public class SomeService
{
public SomeService(HttpClient httpClient)
{
// ...
}
}
// SomeViewComponent.cs
public class SomeViewComponent
{
public SomeViewComponent(SomeService someService)
{
// ...
}
}
As I've already stated, this approach works - the ASP.NET Core DI system is very happy to create the instance of SomeService and its typed HttpClient instance.
To restate the original problem, take the following example code:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeViewComponent>();
// ...
}
public class SomeViewComponent
{
public SomeViewComponent(HttpClient httpClient)
{
// ...
}
}
In this case, the ASP.NET Core DI system refuses to create an instance of SomeViewComponent due to not being able to resolve HttpClient. It turns out that this is not specific just to ViewComponents: it also applies to Controllers and TagHelpers (thanks to Chris Pratt for confirming for TagHelpers).
Interestingly, the following also works:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient<SomeViewComponent>();
// ...
}
public class SomeViewComponent
{
public SomeViewComponent(IHttpClientFactory httpClientFactory)
{
var httpClient = httpClientFactory.CreateClient("SomeViewComponent")
// ...
}
}
In this example, we're taking advantage of the fact that the call to AddHttpClient<SomeViewComponent> registered a named client for us.
In order to be able to inject HttpClient directly into a ViewComponent, we can add a call to AddViewComponentsAsServices when we register MVC with DI:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(...)
.AddViewComponentsAsServices();
// ...
}
AddControllersAsServices and AddTagHelpersAsServices can also be called to add the same support for Controllers and TagHelpers respectively.
If we look at the docs more closely, it's clear that none of the examples there inject a HttpClient into Controllers et al - there's simply no mention of this approach at all.
Unfortunately, I don't know enough about the ASP.NET Core DI system in order to be able to explain exactly why this works the way it does: The information I've provided above simply explains the what along with a solution. Chris Pratt has opened an issue in Github for the docs to be updated to expand upon this.
I was getting a similar error in my Azure Function Version 2. As per this document, we should be able to add the IHttpClientFactory as a dependency. After adding this DI in my Azure Function, I was getting the error mentioned below.
Microsoft.Extensions.DependencyInjection.Abstractions: Unable to
resolve service for type 'System.Net.Http.IHttpClientFactory' while
attempting to activate
'OServiceBus.Adapter.FetchDataFromSubscription1'
The issue was that I had not override the Configure function to add the HttpClient as a registered dependency. So I just created a class called Statup in the root directory of my Azure Function as follows.
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(ServiceBus.Adapter.Startup))]
namespace ServiceBus.Adapter {
public class Startup: FunctionsStartup {
public override void Configure(IFunctionsHostBuilder builder) {
builder.Services.AddHttpClient();
}
}
}
After adding this, my function started working properly. Hope it helps.
I had a similar error message trying to inject a wrapper for an external REST service to my controller as an interface. I needed to change the following in ConfigureServices:
services.AddHttpClient<IMyServiceWrapper>("MyServiceWrapper", client =>
{
client.BaseAddress = new Uri("http://some_service/api");
}
to
services.AddHttpClient<IMyServiceWrapper, MyServiceWrapper>("MyServiceWrapper", client =>
{
client.BaseAddress = new Uri("http://some_service/api");
}
in order to be able to use the interface in the constructor of my controller:
public MyController(IMyServiceWrapper myService)
{
_myService = myService;
}
Useful for testing myController using a mock service.
It seems that you've got two view components mixed up. You're registering the FixturesViewComponent as a "named HTTP client" yet you attempt to inject an HttpClient instance in the ProductsViewComponent.
Changing the HttpClient registration to ProductsViewComponent should help:
services.AddHttpClient<ProductsViewComponent>(options =>
{
options.BaseAddress = new Uri("http://80.350.485.118/api/v2");
});
Maybe it will help, but in my situation this worked:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyService,MyService>(); // my usual DI injection of a service that can be mocked
services.AddHttpClient<IMyService,MyService>(client => {
client.BaseAddress = new Uri("https://myservice.com/api");
}); // notice that I use IMyService for the reference of the registration AND implementation to where it will be injected.
}
public class MyService
{
public MyService(HttpClient client)
{
// client.BaseAddress is properly set here
}
}
public class MyController : Controller
{
public MyController(IMyService service) // used by the interface
{}
}
I've tried services.AddHttpClient<IMyService>() as well, which would not resolve due to lack of it's constructor.
Also tried services.AddHttpClient<MyService>() as above, but it would not resolve the configured instance, as described above.
So the important part is that class that is used to reference the resolved type needs to be used. So this also works:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<MyService>(); // registering the type itself, not via interface
services.AddHttpClient<MyService>(client => {
client.BaseAddress = new Uri("https://myservice.com/api");
}); // it's ok here, since it will be resolved by it's own type name
}
public class MyService
{
public MyService(HttpClient client)
{
// client.BaseAddress is properly set here
}
}
public class MyController : Controller
{
public MyController(MyService service) // used by the type directly
{}
}
It kind of makes sense, but documentation and examples could be better.

Dependency Injection wcf

I want inject a implementation of my Interface in the WCF but I want initialize my container of Dependency Injection in the Client of the WCF. So I can have a different implementation for each client of the my service.
When you use svcutil.exe or the Add Service Reference wizard in Visual Studio, one of the many types auto-generated will be a client interface. Let's call it IMyService. There will also be another auto-generated interface called something like IMyServiceChannel that implements IMyService and IDisposable. Use this abstraction in the rest of your client application.
Since you want to be able to create a new channel and close it again, you can introduce an Abstract Factory:
public interface IMyServiceFactory
{
IMyServiceChannel CreateChannel();
}
In the rest of your client application, you can take a dependency on IMyServiceFactory:
public class MyClient
{
private readonly IMyServiceFactory factory;
public MyClient(IMyServiceFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException("factory");
}
this.factory = factory;
}
// Use the WCF proxy
public string Foo(string bar)
{
using(var proxy = this.factory.CreateChannel())
{
return proxy.Foo(bar);
}
}
}
You can create a concrete implementation of IMyServiceFactory that wraps WCF's ChannelFactory<T> as an implementation:
public MyServiceFactory : IMyServiceFactory
{
public IMServiceChannel CreateChannel()
{
return new ChannelFactory<IMyServiceChannel>().CreateChannel();
}
}
You can now configure your DI Container by mapping IMyServiceFactory to MyServiceFactory. Here's how it's done in Castle Windsor:
container.Register(Component
.For<IMyServiceFactory>()
.ImplementedBy<MyServiceFactory>());
Bonus info: Here's how to wire up a WCF service with a DI Container.
Here is what I understand from your question:
You have an interface that is not related to WCF. Let's call it IInterface
You have a WCF client that used a service. Let's call the service contract: IService
you want the ServiceClient class that by default implements the IService when you add a service reference also to implement IInterface.
IF this is the case, you can use the fact that the ServiceClient class is marked as partial.
Just make another partial declaration for ServiceClient and add the interface you need (You have to make sure that the namespaces are equal for the auto-generated code and your code). It should look somthing like:
namespace [ServiceClient Namespace]
{
public partial class ServiceClient : IInterface
{
}
}
Hope it helped.

Categories