为啥 C# HttpClient 不能调用这个 URL(总是超时)?

Posted

技术标签:

【中文标题】为啥 C# HttpClient 不能调用这个 URL(总是超时)?【英文标题】:Why can the C# HttpClient not call this URL (always times out)?为什么 C# HttpClient 不能调用这个 URL(总是超时)? 【发布时间】:2018-02-14 15:13:59 【问题描述】:

我一直在开发一个确定网页信息的应用程序。其中一个组件涉及向 URL 发出 HTTP GET 请求,获取 html 并对其进行分析。这对我抛出的每个 URL 都有效,除了一个......

罪魁祸首是 .NET HttpClient,它似乎总是在请求问题域中的任何 URL 时超时。但是,浏览器请求的相同 URL 会在几毫秒内返回内容。标题似乎没有什么不寻常的地方。

延长超时时间只会导致需要更长的时间来进行爆破。我已经尝试了几分钟,结果相同。我尝试了各种方法,例如将用户代理字符串设置为 Chrome 的字符串,但无济于事。

有问题的域是:http://careers.adidas-group.com 请注意,同一站点也在https://careers.adidas-group.com 的 HTTPS 上运行(它具有有效的证书)。 使用任一协议都会导致相同的错误。

我可以用一个简单的 C# 控制台应用程序来显示问题,如下所示:

static void Main(string[] args)

    string url = "http://careers.adidas-group.com";

    var client = new HttpClient
    
        Timeout = TimeSpan.FromSeconds(10)
    ;

    using (var message = new HttpRequestMessage(HttpMethod.Get, url))
    
        using (var httpResponse = Task.Run(() => client.SendAsync(message)).Result)
        
            Console.WriteLine("0: 1", httpResponse.StatusCode, httpResponse.ReasonPhrase);
        
    

    Console.ReadLine();

请注意,在上面的示例中,我将超时设置为 10 秒,只是为了加快解决问题的速度 - 但是,增加超时没有任何区别。

具有不同 URL(例如 https://***.com/)的相同代码运行良好。

另请注意,上面的代码已简化为作为控制台应用程序运行。我的实际代码在异步 MVC 控制器方法中异步正确运行(使用等待) - 我只是使用 Task.Run(() => ) 使其与示例中的同步 Main 方法的上下文一起工作。但这对结果没有任何影响。 (实际的例外是“任务已取消”,但这似乎是超时的症状,而不是实际问题)。

谁能向我解释为什么会发生这种情况(与服务器配置有关吗?)以及我可以做些什么来使 HttpClient 满足请求?谢谢。

【问题讨论】:

会是用户代理头吗? 站点测试工具也会发生:redbot.org/?uri=http%3A%2F%2Fcareers.adidas-group.com%2F @JulianReschke 不,我没有,但有趣的是,使用该工具也会出现同样的超时问题。服务器必须拒绝“机器人”,但我不清楚什么标准。 curl 的结果相同 @maccettura 这是我最初的想法之一,但我尝试在一些流行的浏览器代理的请求中设置user-agent 字符串,但没有任何区别。 【参考方案1】:

好的,经过大量调查,我决定一定是服务器在请求中寻找特定的标头。因此,我检查了大多数浏览器发送的内容,复制了这些内容,然后最终将其缩减到要求 all 存在以下标头的服务器:

client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
client.DefaultRequestHeaders.Add("Accept-Language", "en-GB,en;q=0.9,en-US;q=0.8");

删除其中任何一个,服务器将不会响应。很奇怪!

感谢所有看过这个的人,我希望这个答案将来可以帮助别人:)

编辑 - 更多怪异

好的,现在奇怪的事情仍在继续,因为即使这解决了在本地运行的问题(在 VS 2017 中使用 IIS Express),但在部署到实时环境(在 IIS 7.5 / Windows Server 中运行)时仍然无法正常工作。与控制台应用程序版本相同 - 适用于本地 PC,不适用于服务器。尝试了 3 台 Windows 服务器,相同的代码,它在一台上运行,而在另外两台上却不行。离奇。

进一步编辑 - 解决方案?

因此,在进一步阅读之后,certain web-servers 出现,例如 akamai ghost(托管相关域)有一些相当复杂的“机器人”检测,它拒绝来自未知客户端的连接。措施包括检查 HTTP 请求标头的顺序,以便它们与用户代理通常发送的内容相匹配(即,如果您将用户代理字符串伪装成 Chrome,您最好完全像 Chrome 一样,发送标头按照 chrome 执行和接受相同内容类型等的顺序)。

尝试伪造大量浏览器用户代理字符串后,我最终发现“假装”为 Google PageSpeed bot 是有效的,即。将用户代理字符串设置为:“Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko; Google Page Speed Insights) Chrome/27.0.1453 Safari/537.36

无论使用什么版本的 Windows 服务器或 .NET Framework,这似乎都有效。

我最终想出的标题是:

this.Client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/apng,*/*;q=0.8");
this.Client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
this.Client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
this.Client.DefaultRequestHeaders.Add("Accept-Language", "en-GB,en;q=0.9,en-US;q=0.8");
this.Client.DefaultRequestHeaders.Add("Connection", "keep-alive");
this.Client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
this.Client.DefaultRequestHeaders.Add("Pragma", "no-cache");
this.Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko; Google Page Speed Insights) Chrome/27.0.1453 Safari/537.36");

【讨论】:

@Evk 是的,你是对的——那不是“必需的”之一。我将编辑我的答案。 我虽然在这里遇到了和你一样的事情,但我快疯了。惊人的修复,谢谢!【参考方案2】:

你得出的答案是正确的。然而,为了将来的通知,我建议使用像 Charles 或 Fiddler 这样的 web 调试器。它可以更轻松地复制您的请求,并最终找到您没有从主机获得任何响应的根源。在这个例子中,我使用了 Charles。

从我的 Visual Studio 调试器中,我可以看到客户端“DefaultHeaders”都是空的。所以现在 OP 已经展示了我们需要做的就是将标头添加到我们的客户端并希望它满足主机。

static void Main(string[] args)

    string url = "http://careers.adidas-group.com";

    var client = new HttpClient
    
        Timeout = TimeSpan.FromSeconds(10)
    ;

    client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
    client.DefaultRequestHeaders.Add("Accept-Language", "en-US,en;q=0.5");
    client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0");
    client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");

    using (var message = new HttpRequestMessage(HttpMethod.Get, url))
    

        using (var httpResponse = Task.Run(() => client.SendAsync(message)).Result)
        
            Console.WriteLine("0: 1", httpResponse.StatusCode, httpResponse.ReasonPhrase);
        
    

    Console.ReadLine();

我只想添加那些我知道对大多数主机至关重要的内容。测试上面的代码,我们得到一个代码“OK:OK”。如果我们尝试删除以下任何一行:

client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
client.DefaultRequestHeaders.Add("Accept-Language", "en-US,en;q=0.5");
client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");

我们将再次陷入死循环。这意味着主机不关心您使用的用户代理。阿迪达斯的 robots.txt(https://careers.adidas-group.com/robots.txt) 也表明了这一点——这表明(因为数据挖掘器是不使用浏览器的自动化服务)——阿迪达斯不介意拥有几个其域周围的蜘蛛/数据挖掘者。

【讨论】:

感谢您的意见。我确实尝试过使用 Fiddler,但它在我的工作网络上存在问题。我还没有听说过查尔斯,所以会检查一下。干杯! 我也观察到了同样的行为。添加对 Kent 在这里所写内容的确认。

以上是关于为啥 C# HttpClient 不能调用这个 URL(总是超时)?的主要内容,如果未能解决你的问题,请参考以下文章

修改每个请求的请求标头 C# HttpClient PCL

C# Xamarin 文件上传到 API 可以使用 RestSharp,但不能使用 HttpClient

C#中HttpClient使用注意:预热与长连接

C#中HttpClient使用注意:预热与长连接

C# 中的 httpClient 调用超时,而 cUrl 正在工作

第二次调用 C# HttpClient 错误请求