初始化 HttpClient 的最佳实践
初始化 HttpClient 的最佳实践
这是一个虚构的场景,我想找到最佳实现方式,并了解最佳实践。\n我正在编写一个服务,该服务向需要授权头的API发出GET请求,问题是我为每个服务初始化一个新的客户端,我在思考是否最好使用单例客户端,并在不同的服务中使用它。\n这里有我的BlogService,我在这里初始化客户端,然后添加授权头,用户名和密码将在配置中,但这只是一个例子。\n代码如下:\n
namespace MyDemoProject.Services { public class BlogService : IBlogService { private readonly HttpClient client; private string username = "myUsername"; private string password = "myPassword"; private static string url = "https://example.com/api/blogs"; public BlogService() { client = new HttpClient(); string encoded = System.Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)); client.DefaultRequestHeaders.Add("Authorization", "Basic " + encoded); } public async TaskGetAllBlogs() { var request = await client.GetAsync(url); var data = await request.Content.ReadAsStringAsync(); return data; } } }
\n这里是我的CommentService,非常相似,但是使用了不同的URL。这只是一个简单的例子,它可以包含在同一个BlogService中,但假设我希望将其分解以便于可维护性和可扩展性。\n代码如下:\n
namespace MyDemoProject.Services { public class CommentService : ICommentService { private readonly HttpClient client; private string username = "myUsername"; private string password = "myPassword"; private static string url = "https://example.com/api/comments"; public CommentService() { client = new HttpClient(); string encoded = System.Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)); client.DefaultRequestHeaders.Add("Authorization", "Basic " + encoded); } public async TaskGetAllComments() { var request = await client.GetAsync(url); var data = await request.Content.ReadAsStringAsync(); return data; } } }
\n是否有更好的实现方式?这种实现方式是否存在任何问题?
使用HttpClient的最佳实践是避免手动创建实例,因为这可能会导致DNS缓存和TCP连接未被回收而引起不必要的问题。
解决方法是使用HttpClientFactory,它会为您创建和维护HttpClient。
首先需要从nuget添加依赖Microsoft.Extensions.DependencyInjection。
如果使用依赖注入,将以下代码添加到Startup文件中的ConfigureServices方法中:
public void ConfigureServices(IServiceCollection services) { services.AddScoped<BearerTokenHandler>(); services.AddHttpClient<ICommentService, CommentService>() .ConfigureHttpClient(client => { client.BaseAddress = new Uri("https://example.com/api"); }) .AddHttpMessageHandler<BearerTokenHandler>(); }
然后,在MyDemoProject.Services命名空间下创建BearerTokenHandler类和CommentService类。
BearerTokenHandler类继承自DelegatingHandler,并重写SendAsync方法,用于添加身份验证头信息。
namespace MyDemoProject.Services { public class BearerTokenHandler : DelegatingHandler { private const string BasicAuthenticationScheme = "Basic"; private string username = "myUsername"; private string password = "myPassword"; public BearerTokenHandler() { } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { string encoded = System.Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)); request.Headers.Authorization = new AuthenticationHeaderValue(BasicAuthenticationScheme, token); return await base.SendAsync(request, cancellationToken); } } }
CommentService类实现了ICommentService接口,并在构造函数中注入HttpClient实例。
namespace MyDemoProject.Services { public class CommentService : ICommentService { private readonly HttpClient client; public CommentService(HttpClient httpClient) { client = httpClient; } public async Task<string> GetAllComments() { string response = null; using (var request = new HttpRequestMessage(HttpMethod.GET, "comments")) { response = await client.SendAsync(request); } var data = await response.Content.ReadAsStringAsync(); return data; } } }
通过使用HttpClientFactory和依赖注入,可以更好地初始化和管理HttpClient,避免了手动创建HttpClient实例可能引起的问题。
在.NET 6中,最佳实践是使用IHttpClientFactory来避免每次使用HttpClient时都创建新的实例,以及避免代码重复。
以下是一个基于您的代码的简单示例。
首先,您需要注册和配置IHttpClientFactory,如下所示:
_ = services.AddHttpClient("MyHttpClientKey", (serviceProvider, httpClient) => { private string username = "myUsername"; private string password = "myPassword"; string encoded = System.Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)); _ = httpClient.BaseAddress = new Uri("https://example.com/api/comments"); _ = httpClient.Timeout = TimeSpan.FromSeconds(30); httpClient.DefaultRequestHeaders.Add("Authorization", "Basic " + encoded); });
然后,您可以在类中注入HttpClient并使用它:
public class CommentService : ICommentService { private readonly HttpClient client; public CommentService(IHttpClientFactory httpClientFactory) { httpClient = httpClientFactory.CreateClient("MyHttpClientKey"); } public async TaskGetAllComments() { var request = await client.GetAsync(url); var data = await request.Content.ReadAsStringAsync(); return data; } }
希望对您有所帮助!