C# 在HttpClient上对自定义数据使用DelegatingHandler

C# 在HttpClient上对自定义数据使用DelegatingHandler,c#,.net-core,dotnet-httpclient,C#,.net Core,Dotnet Httpclient,考虑到使用HttpClient的众所周知的困境和问题,即套接字耗尽和不考虑DNS更新,它认为使用IHttpClientFactory并让容器决定何时以及如何利用http池连接是最佳实践。这很好,但现在我无法在每个请求上用自定义数据实例化自定义DelegatingHandler 以下示例介绍了我在使用工厂方法之前是如何做到这一点的: 公共类HttpClientInterceptor:DelegatingHandler { 私有只读int_id; 公共HttpClientInterceptor(in

考虑到使用HttpClient的众所周知的困境和问题,即套接字耗尽和不考虑DNS更新,它认为使用IHttpClientFactory并让容器决定何时以及如何利用http池连接是最佳实践。这很好,但现在我无法在每个请求上用自定义数据实例化自定义DelegatingHandler

以下示例介绍了我在使用工厂方法之前是如何做到这一点的:

公共类HttpClientInterceptor:DelegatingHandler
{
私有只读int_id;
公共HttpClientInterceptor(int id)
{
_id=id;
}
受保护的覆盖异步任务SendAsync(HttpRequestMessage请求,CancellationToken CancellationToken)
{
//将id与此请求关联
数据库。插入查询(_id,请求);
返回wait base.sendaync(请求、取消令牌);
}
}
每次我实例化HttpClient时,都可以传递一个Id:

public void DoEnquiry()
{
//假设插入新查询
int id=Database.insertnewinquiry();
使用(var http=newhttpclient(newhttpclientinterceptor(id)))
{
//并在http客户端上执行一些操作
//将记录在与id关联的数据库中
http.GetStringAsync(“http://url.com");
}
}
但是现在我无法实例化HttpClient和处理程序

public void DoEnquiry(IHttpClientFactory工厂)
{
int id=Database.insertnewinquiry();
var http=factory.CreateClient();
//现在呢??
http.GetStringAsync(“http://url.com");
}
我如何使用工厂实现类似的目标

我无法在每个请求上用自定义数据实例化自定义DelegatingHandler

这是正确的。但您可以使用可重用(且无状态)的自定义
DelegatingHandler
,并将数据作为请求的一部分进行传递。这就是为什么。在执行这些类型的“自定义上下文”操作时,我更喜欢将上下文“属性”定义为扩展方法:

public static class HttpRequestExtensions
{
  public static HttpRequestMessage WithId(this HttpRequestMessage request, int id)
  {
    request.Properties[IdKey] = id;
    return request;
  }

  public static int? TryGetId(this HttpRequestMessage request)
  {
    if (request.Properties.TryGetValue(IdKey, out var value))
      return value as int?;
    return null;
  }

  private static readonly string IdKey = Guid.NewGuid().ToString("N");
}
然后您可以在(可重用的)
DelegatingHandler
中使用它:

public class HttpClientInterceptor : DelegatingHandler
{
  protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  {
    var id = request.TryGetId();
    if (id == null)
      throw new InvalidOperationException("This request must have an id set.");

    // associate the id with this request
    Database.InsertEnquiry(id.Value, request);
    return await base.SendAsync(request, cancellationToken);
  }
}
呼叫样板相当难看。您可以将其封装到自己的
GetStringAsync
便利方法中;像这样的方法应该会奏效:

public static class HttpClientExtensions
{
  public static async Task<string> GetStringAsync(int id, string url)
  {
    var request = new HttpRequestMessage(HttpMethod.Get, url);
    request.SetId(id);
    var response = await client.SendAsync(request);
    return await response.Content.ReadAsStringAsync();
  }
}

你为什么要求一个新的客户机而不是让框架通过依赖注入为你处理这个问题呢?例如,
public void DoEnquiry(HttpClient-client)
@Jimenemex
IHttpClientFactory
现在是大多数情况下的正确方法()另一个解决方案是以某种方式关闭HttpClient的底层连接,以便释放套接字连接。但是找不到有关此HttpRequestMessage的任何内容。属性是我正在查找的,谢谢。@stepen我还有一个关于HttpClient的问题,请说明您可能可以提供帮助。对于遇到此问题的其他人:In.Net5
HttpRequestMessage.Properties
已被弃用,您应该使用
.Options
属性。但无论如何你都会发现:)
public static class HttpClientExtensions
{
  public static async Task<string> GetStringAsync(int id, string url)
  {
    var request = new HttpRequestMessage(HttpMethod.Get, url);
    request.SetId(id);
    var response = await client.SendAsync(request);
    return await response.Content.ReadAsStringAsync();
  }
}
public async Task DoEnquiry(IHttpClientFactory factory)
{
  int id = Database.InsertNewEnquiry();
  var http = factory.CreateClient();
  var result = await http.GetStringAsync(id, "http://url.com");
}