是否可以在 C# 的 HttpClient 中设置自定义 DNS 解析器

Dyl*_*0th 9 .net c# dns dotnet-httpclient

我到底想要什么:

public static CustomDnsResolver : Dns 
{
   .....
}

public static void Main(string[] args) 
{
    var httpClient = new HttpClient();
    httpClient.Dns(new CustomDnsResolver());
}
Run Code Online (Sandbox Code Playgroud)

基本上我只是想在 HttpClient 中使用我的自定义 DNS 解析器而不是系统默认解析器,有什么方法可以实现它吗?

小智 7

不确定这是否适用于所有情况,但它对我的情况非常有效,即使使用 HTTPS 时也是如此。因此,您要做的就是将 URL 中的主机替换为自定义解析器已解析的实际 IP 地址,然后只需添加带有主机名的“主机”标头即可。像这样:

        var requestUri = new Uri("https://123.123.123.123/some/path");
        using var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        request.Headers.TryAddWithoutValidation("host", "www.host-name.com");
        using var response = await httpClient.SendAsync(request);
Run Code Online (Sandbox Code Playgroud)

我希望这会有所帮助,因为这是我第一次遇到这个问题,而且之前我从未能够解决它。

  • 我已经深入研究了 SocketsHttpHandler 的行为变化,正如您在 https://github.com/dotnet/runtime/issues/45144 中看到的,实际原因是服务器在 .NET 之前需要 SNI 验证从 .5 开始,主机头不会自动用作 SNI,但这种行为在 .NET 5 中发生了变化,现在如果设置了主机头,它会强制将主机头用作 SNI。 (4认同)

Kal*_*ten 6

您所拥有的用例正是 Microsoft 构建 HttpClient 堆栈的原因。它允许您借助HttpMessageHandler类将业务逻辑放入分层类中。您可以在ms docsVisualstudiomagazine中找到一些示例

async Task Main()
{
    var dnsHandler = new DnsHandler(new CustomDnsResolver());
    var client = new HttpClient(dnsHandler);
    var html = await client.GetStringAsync("http://google.com");
}

public class DnsHandler : HttpClientHandler
{
    private readonly CustomDnsResolver _dnsResolver;

    public DnsHandler(CustomDnsResolver dnsResolver)
    {
        _dnsResolver = dnsResolver;
    }
    
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var host = request.RequestUri.Host;
        var ip = _dnsResolver.Resolve(host);
        
        var builder = new UriBuilder(request.RequestUri);
        builder.Host = ip;
        
        request.RequestUri = builder.Uri;
        
        return base.SendAsync(request, cancellationToken);
    }
}

public class CustomDnsResolver
{
    public string Resolve(string host)
    {
        return "127.0.0.1";
    }
}
Run Code Online (Sandbox Code Playgroud)

基于Meziantou 博客文章的更高级版本

async Task Main()
{
    var dnsHandler = new DnsHandler(new CustomDnsResolver());
    var client = new HttpClient(dnsHandler);
    var html = await client.GetStringAsync("http://google.com");
}

public class DnsHandler : HttpClientHandler
{
    private readonly CustomDnsResolver _dnsResolver;

    public DnsHandler(CustomDnsResolver dnsResolver)
    {
        _dnsResolver = dnsResolver;
    }
    
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var host = request.RequestUri.Host;
        var ip = _dnsResolver.Resolve(host);
        
        var builder = new UriBuilder(request.RequestUri);
        builder.Host = ip;
        
        request.RequestUri = builder.Uri;
        
        return base.SendAsync(request, cancellationToken);
    }
}

public class CustomDnsResolver
{
    public string Resolve(string host)
    {
        return "127.0.0.1";
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您尝试从 https 站点获取数据,则此解决方案不起作用。 (4认同)
  • 这并不能回答问题。您不能只是说“覆盖证书验证”,我们期望的是我们正确设置主机名,以便 SSL 工作但避免 DNS 查找。 (2认同)