I know I'll get crucified for asking this question that has been asked a million times before and I promise you that I've looked at most of those questions/answers but I'm still a bit stuck.
This is a .NET Standard 2.0 class library supporting an ASP.NET Core 6 API.
In my Program.cs I create a named HttpClient like this:
builder.Services.AddHttpClient("XYZ_Api_Client", config =>
{
var url = "https://example.com/api";
config.BaseAddress = new Uri(url);
});
I have a custom client that will use this HttpClient and I create a singleton MyCustomClient in Program.cs so that my repositories can use it. The code is below. This is where I'm stuck as I'm not sure how to pass my named HttpClient into MyCustomClient.
builder.Services.AddSingleton(new MyCustomClient(???)); // I think I need to pass the HttpClient to my CustomClient here but not sure how
And my CustomClient needs to use this HttpClient named XYZ_Api_Client to do its job:
public class MyCustomClient
{
private readonly HttpClient _client;
public MyCustomClient(HttpClient client)
{
_client = client;
}
public async Task<bool> DoSomething()
{
var result = await _client.GetAsync();
return result;
}
}
So I'm not sure how I can pass this named HttpClient into MyCustomClient in the Program.cs.
You can directly inject the IHttpClientFactory in you class, and then assign the named HttpClient to a property.
Register the factory and your custom client:
builder.Services.AddHttpClient("XYZ_Api_Client", config =>
{
var url = "https://example.com/api";
config.BaseAddress = new Uri(url);
});
// no need to pass anything, the previous line registered IHttpClientFactory in the container
builder.Services.AddSingleton<MyCustomClient>();
And then in you class:
public class MyCustomClient
{
private readonly HttpClient _client;
public MyCustomClient(IHttpClientFactory factory)
{
_client = factory.CreateClient("XYZ_Api_Client");
}
// ...
}
Or, you can pass the named instance when registering MyCustomClient
Register the factory and your custom client:
builder.Services.AddHttpClient("XYZ_Api_Client", config =>
{
var url = "https://example.com/api";
config.BaseAddress = new Uri(url);
});
// specify the factory for your class
builder.Services.AddSingleton<MyCustomClient>(sp =>
{
var factory = sp.GetService<IHttpClientFactory>();
var httpClient = factory.CreateClient("XYZ_Api_Client");
return new MyCustomClient(httpClient);
});
And then in you class:
public class MyCustomClient
{
private readonly HttpClient _client;
public MyCustomClient(HttpClient client)
{
_client = client;
}
// ...
}
You can also do this:
// register the named client with the name of the class
builder.Services.AddHttpClient("MyCustomClient", config =>
{
config.BaseAddress = new Uri("https://example.com/api");
});
// no need to specify the name of the client
builder.Services.AddHttpClient<MyCustomClient>();
What AddHttpClient<TClient>(IServiceCollection) does is
Adds the IHttpClientFactory and related services to the IServiceCollection and configures a binding between the TClient type and a named HttpClient. The client name will be set to the full name of TClient.
You can find the full documentation here.
Related
I want to pass HttpMessageHandler into my HttpClient. The main reason is to pass the proxy (IWebProxy).
Ideally, HttpClient is defined in constructor like this (Using DI):
public class MyApiClient : IMyApiClient
{
private readonly HttpClient _httpClient;
public MyApiClient(HttpClient httpClient)
{
// I am not sure how to pass `handler` here.
_httpClient = httpClient;
}
}
The way I am adding proxy into HttpClient is as following (Not using DI):
public class MyApiClient : IMyApiClient {
private readonly HttpClient _httpClient;
public MyApiClient() {
var proxy = new WebProxy {
Address = new Uri("http://test.com:1234"),
BypassProxyOnLocal = false,
UseDefaultCredentials = false,
};
// Create a client handler that uses the proxy
var httpClientHandler = new HttpClientHandler {
Proxy = proxy,
};
// Disable SSL verification
httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
_httpClient = new HttpClient(handler: httpClientHandler, disposeHandler: true);
}
}
Questions:
Is my approach correct to pass proxy?
Is it possible to pass proxy in HttpClient using DI (first approach)?
Manually handling either HttpClient or the underlying HttpMessageHandler is considered a bad practice in general due to the way the low level socket resources are managed: in most cases, it would either lead to socket starvation due to leaks, or it could lead to unusable failed connections.
Instead, you should rely on IHttpClientFactory and related abstractions. First, I'd strongly recommend reading the guidelines from Microsoft themselves here:
IHttpClientFactory with .NET
The simplest possible way to configure a single handler is to use the AddHttpClient overload. In your case, it would look something like the following:
services
.AddHttpClient<IMyApiClient, MyApiClient>((provider, client) =>
{
// Confiure one-off settings of the client here.
// There is an overload which doesn't pass the 'provider', in case
// you don't need DI during the configuration.
//
// For example (using recommended 'IOptions' approach):
var settings = provider.GetRequiredService<IOptions<MyApiSettings>>();
client.BaseAddress = settings.Value.ApiRoot;
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
// Add whatever logic to create the handler here
// Similar to the other method, this also has DI-enabled overloads.
var proxy = new WebProxy
{
Address = new Uri("http://test.com:1234"),
BypassProxyOnLocal = false,
UseDefaultCredentials = false,
};
// Create a client handler that uses the proxy
var httpClientHandler = new HttpClientHandler { Proxy = proxy };
// Disable SSL verification
httpClientHandler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
return httpClientHandler;
});
This will automatically link the configured HttpClient instance with your MyApiClient in the container, so that you can then directly inject HttpClient in the MyApiClient constructor like in your first example:
public class MyApiClient : IMyApiClient
{
private readonly HttpClient _httpClient;
public MyApiClient(HttpClient httpClient)
{
// It will work now ;)
_httpClient = httpClient;
}
}
In my .Net core application I have NamedHttpClient and I have a TypedHttpClient. I need to use the NamedClient as the default httpclient inside TypedHttpClient.
My configure services:
public static IServiceCollection ConfigureServiceOptions(this IServiceCollection services, IConfiguration config)
{
IConfigurationSection serverSection = config.GetSection(nameof(ServerOptions));
services.Configure<ServerOptions>(serverSection);
//Named
services.AddHttpClient("defaultHttpClient", client =>
{
client.BaseAddress = new System.Uri(dataServerSection.Get<ServerOptions>().ServerUrl);
client.DefaultRequestHeaders.Add("Accept", "application/xml, */*");
client.DefaultRequestHeaders.Add("User-Agent", "Server v1.0.0");
});
}
//startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureServiceOptions(Configuration);
//Adding Typed
services.AddHttpClient<DataService>(); //I need to use defaultHttpClient as HttpClient Inside DataService
}
So far I found 3 ways to do this, not sure if there is any pitfall in any of the method
Way 1:
services.AddHttpClient<DataService>();
public class DataService
{
private HttpClient Client { get;}
public DataService(/*need to pass this otherwise cant resolve*/ HttpClient client, IHttpClientFactory factory)
{
Client = factory.CreateClient("defaultHttpClient");
}
}
In Way 1 i need to have the additional HttpClient client parameter in the constructor. Otherwise the DataService is not resolved.
Way2:
services.AddTransient<DataService>( cfg =>
{
var clientFactory = cfg.GetRequiredService<System.Net.Http.IHttpClientFactory>();
var httpClient = clientFactory.CreateClient("defaultHttpClient");
return new DataService(httpClient);
});
public class DataService
{
private HttpClient Client { get;}
public DataService(HttpClient client)
{
Client = client;
}
}
Way 3:
services.AddTransient<DataService>();
public class DataService
{
private HttpClient Client { get;}
public DataService(IHttpClientFactory factory)
{
Client = factory.CreateClient("defaultHttpClient");
}
}
I think Way2 and Way3 might be same. Not sure if there is any difference.
Can someone tell what is the recommended way ? or if there is any other way?
Since you are not actually setting up the typed client for DataService the "Way 1" does not make much sense, it is basically "Way 3" but with extra steps.
"Way 2" make service code cleaner but will become cumbersome to maintain if new dependencies will be needed for it later.
"Way 3" is how using named HttpClient is shown in the docs so I would go with it.
I would like to preface this post with the fact that I am VERY new to C#, .NET Core, and Blazor after being a long-time Java person.
I am following along with Microsoft's documentation on Make HTTP requests using IHttpClientFactory in ASP.NET Core, but I cannot seem to understand how to use this with my application.
I have a IP address for an API endpoint, So I created a Named Client:
services.AddHttpClient("TheAPIEndpoint", client =>
{
client.BaseAddress = new Uri("192.168.1.234");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("User-Agent", "Skynet v1.0");
});
Next, I went to the "Basic Usage" section of the documentation at the top of the page where it says:
An IHttpClientFactory can be requested using dependency injection (DI). The following code uses IHttpClientFactory to create an HttpClient instance
It says to create a constructor for my class and pass in an IHttpClientFactory:
public class BasicUsageModel : PageModel
{
private readonly IHttpClientFactory _clientFactory;
public BasicUsageModel(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task OnGet()
{
var client = _clientFactory.CreateClient();
}
}
Here is where my issue "begins". When you create a new Razor component for a Blazor application, the generated .RAZOR file looks like this:
<h3>HelloWorld</h3>
#code {
}
When I need to call the API, I need to create this method:
private async Task GetSearchResults()
{
// ...
}
All together, it looks like this:
<h3>HelloWorld</h3>
#if (fetchedResults != null)
{
#foreach (var result in fetchedResults)
{
<p>#(result.someData)</p>
}
}
#code {
private async Task GetSearchResults()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(#"http://192.168.1.234:8082/");
// ...
fetchedResults = JsonConvert.DeserializeObject<ResultSet>(response);
}
}
As you see, I don't have a "normal" class structure in which to create the constructor, to which to pass an IHttpClientFactory parameter, with which I can create a new HttpClient.
So, how do I go about doing this?
Blazor does not support constructors as you've already discovered, instead it provides the #inject directive to support dependency injection. The syntax is:
#inject <dependency-type-name> <variable-name>
In your case:
#inject IHttpClientFactory _clientFactory
<h3>HelloWorld</h3>
#if (fetchedResults != null)
{
#foreach (var result in fetchedResults)
{
<p>#(result.someData)</p>
}
}
#code {
private async Task GetSearchResults()
{
var client = _clientFactory.CreateClient();
client.BaseAddress = new Uri(#"http://192.168.1.234:8082/");
// ...
fetchedResults = JsonConvert.DeserializeObject<ResultSet>(response);
}
}
Don't forget to register your dependencies in the Startup.ConfigureServices method!
I am trying to setup logging in my server side Blazor app.
Here is a class I have that gets data from an API:
public class LicenseService
{
private ILogger _logger;
public LicenseService(ILogger<LicenseService> logger)
{
_logger = logger;
}
public async Task<List<Licenses>> GetLicensesAsync()
{
List<Licenses> model = null;
var client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
var task = await client.GetAsync("https://my.domain/v1/licenses");
if(!task.IsSuccessStatusCode)
{
_logger.LogError($"Result was {task.StatusCode}");
}
_logger.LogError($"Result was {task.StatusCode}");
var jsonString = await task.Content.ReadAsStringAsync();
model = JsonConvert.DeserializeObject<List<Licenses>>(jsonString);
return model;
}
}
Here is a .razor page that calls that class. What do I pass in to new LicenseService();? I can't instantiate a new instance of Ilogger or Ilogger<LicenseService>.
private async Task<List<Licenses>> GetLicenseData()
{
LicenseService service = new LicenseService();
var licenseData = await service.GetLicensesAsync();
return licenseData;
}
I'm sure this is because I don't really understand how dependency injection works in dotnet core.
Ideally, if you are using dependency injection as it's designed, you wouldn't be creating a new instance of LicenseService there at all, but rather injecting the dependency, as the name suggests.
During your Startup, you might have the following service registration (or something similar).
services.AddScoped<LicenseService>();
Within your Razor page's constructor, the service container can now automatically resolve this dependency for you, taking care of all of its dependencies, too, assuming those dependencies were also registered.
public class YourPageModel : PageModel
{
private readonly LicenseService _licenseService;
public YourPageModel (LicenseService licenseService)
{
_licenseService = licenseService;
}
public async Task<List<Licenses>> GetLicenseData()
{
var licenseData = await _licenseService.GetLicensesAsync();
return licenseData;
}
}
Update:
The service can be injected into your Blazor component with DI using this syntax at the top:
#inject LicenseService licenseService
And then referenced as licenseService in your method. See this link for an example:
https://learn.microsoft.com/en-us/aspnet/core/tutorials/build-your-first-blazor-app?view=aspnetcore-3.1#dependency-injection
We are currently using the HttpClient to invoke the Web APIs from the MVC application.
The HttpClient is part of a static helper class as shown below
public static class ApiClient
{
private static HttpClient MyHttpClient()
{
HttpClient client = new HttpClient();
...
return client;
}
public static T HttpGet<T>(string requestUri)
{
using (var client = MyHttpClient())
{
...
}
}
}
and it is invoked from the MVC controller as given below
ApiClient.HttpGet<MyModel>("<<API URL>>");
So whenever the ApiClient is invoked, a new underlying connection will be opened which isn't the right way.
I read about HttpClientFactory and read this post and I resulted in modifying the creation logic as
private static HttpClient MyHttpClient()
{
var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
var client = httpClientFactory.CreateClient();
//HttpClient client = new HttpClient();
...
return client;
}
Will this prevent from opening multiple connections even if invoked multiple times?
The IHttpClientFactory functionality is predicated on dependency injection, and statics are fundamentally incompatible with dependency injection. As the docs clearly show, the correct way to do this is:
public class ApiClient
{
private readonly HttpClient _client;
public ApiClient(HttpClient client)
{
_client = client;
}
...
}
And then you register this service in ConfigureServices:
services.AddHttpClient<ApiClient>(c => { ... });
The client class should not be static and there's no reason for it to be static.