【问题标题】:C# MVC - Not able to retreive JSON data from Server WebAPI url using webClientC# MVC - 无法使用 webClient 从服务器 WebAPI url 检索 JSON 数据
【发布时间】:2020-01-13 10:33:36
【问题描述】:

我正在尝试从 WEB API 加载 JSON 数据。如果我从任何浏览器请求数据,URL 工作正常。 如果我尝试使用 webClient 获取数据,则相同的 URL 不起作用。 它抛出以下错误:

A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) +111    
[IOException: Unable to read data from the transport connection: A connection attempt failed     because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.]

System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size) +299
    [WebException: The underlying connection was closed: An unexpected error occurred on a receive.]
       System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request) +298
       System.Net.WebClient.DownloadString(Uri address) +106

我发现这样可能会堆栈溢出类似的问题并实施了 TSL 安全协议和标头更改,但仍然无法正常工作。

下面是我的代码:

 PCR IWebClientServices<PCR>.PrepareWebClient(string PreparedURL)
    {
        try
        {
            //this code bypass the SSL exception
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | 
                                                   SecurityProtocolType.Tls11 | 
                                                   SecurityProtocolType.Tls;
            //now we will invoke the webClient
            var webClient = new WebClient();
            webClient.Headers.Add(HttpRequestHeader.Cookie, "cookievalue");
            webClient.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0");
            var json = webClient.DownloadString(PreparedURL);
            return JsonConvert.DeserializeObject<PCR>(json);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            return null;
        }
    }

我被困在这里,因为这段代码有时可以正常工作,有时根本无法正常工作。 我不知道出了什么问题,但我认为在请求浏览器请求等数据时我缺少一些东西,但服务器正在捕获一些可疑的请求并且没有响应。但是,如果我从浏览器浏览 URL,它会发送数据。

【问题讨论】:

  • 您是否在客户端/调用方中针对 .NET Framework 4.7.2 或更高版本,默认情况下使用 TLS 支持 1.2,当从服务器中删除对早期协议的支持时,我们遇到了类似的问题。还要在您的配置文件中强制执行此运行时
  • 感谢 Schwarzie2478。是的,我的框架是 4.7.2。在这种情况下我该怎么办? web.config 有哪些变化?

标签: c# model-view-controller webclient webapi


【解决方案1】:

为了支持 TLS 1.2 连接,应以 .NET 4.7.2 为目标。

当您想确保应用程序在某个运行时运行时,请确保您的配置文件中存在以下部分:

App.config

<configuration>

 <startup>
   <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
 </startup>
</configuration>

Web.config

 <system.web>
   <compilation debug="true" targetFramework="4.7.2" /> 
   <httpRuntime targetFramework="4.7.2" maxRequestLength="102400" />
 </system.web>
</configuration>

补充说明:

我不确定这是否是个问题,但 webclient 是一种已弃用的调用 web 服务的方式,您应该更喜欢 httpclient 来发出请求。

【讨论】:

  • 感谢 Schwarzie2478。将在此处检查您的解决方案并进行更新。
  • 嗨 Schwarzie2478。我的 web.config 中已经有这个部分。
  • 还是没有运气:(
【解决方案2】:

最后我能够解决这个问题。 问题是因为服务器已使用 AES128 证书将其 SSL 升级到 TLS 1.2。 我将 webclient 更改为 httpclient,并避免使用 block。

using(var client = new HttpClient())
{
    //I removed this code as using block code serves only one time
    // And was failing for next subsequent request
}

我搜索了很多,发现如果您在 using 块中使用 HttpClient,那么该对象将被释放,但 TCP 连接将保持打开状态。它不会被关闭。 因此,上述方法适用于单个请求,并且由于此问题,下一个后续请求失败。

所以我遵循以下方法,最后我的代码现在像魅力一样工作。

我在我的类中将 HttpClient 对象初始化为静态

private static HttpClient httpClient = new HttpClient();

然后我为我的泛型类调用它,将所有信息传递给它并返回我想要的结果。

以下是完整的工作代码:

public class MyWebClientServices
{
    private static HttpClient httpClient = new HttpClient();

    public T PrepareWebClient<T>(string preparedURL)
    {
        try
        {
          // As my server is using TLS1.2 I kept only that protocol
          ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

          httpClient.DefaultRequestHeaders.Accept.Clear();
          httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualtiyHeaderValue("application/json");
          var json = httpClient.GetStringAsync(preparedURL).Result;
          //here T is generic PCR class. I have created generic classes for which I need to invoke httpClient & their respective urls as I want to use this same code for all.
          //If your class is not generic then instead of T you can use your class
          T tJson = JsonConvert.DeserializeObject<T>(json);
          return (T)Convert.ChangeType(tJson, typeof(T));
         //finally my issue has been fixed and I'm getting data on all subsequent requests too
    }
    catch(Exception e)
    {
        throw e;
    }

}//end of MyWebClientServices class

我参考了以下博客,其中包含与此问题相关的所有信息: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

2) 如果您正在为 httpClient 创建单例模式并进行共享,那么它也有严重的行为。

即 HttpClientHandler 创建一个连接组(以其哈希码命名)并且在被释放之前不会关闭组中的连接。这基本上意味着只要连接打开,DNS 检查就不会发生。

一个天真的解决方案是每次使用 HttpClient(因此是 HttpClientHandler)。

另一种解决方案是在您的 HttpClient 上设置 DefaultRequestHeaders 的 ConnectionClose 属性:

var client = new HttpClient();
client.DefaultRequestHeaders.ConnectionClose = true;

这会将 HTTP 的 keep-alive 标头设置为 false,因此套接字将在单个请求后关闭。事实证明,这可能会为您的每个 HTTP 调用增加大约 35 毫秒(带有长尾,即放大异常值),从而阻止您利用重用套接字的好处。那么解决办法是什么呢?

现在要修复它,我们需要做的就是通过将 URL 传递给端点并设置 ConnectionLeaseTimeout 来获取端点的 ServicePoint 对象:

var sp = ServicePointManager.FindServicePoint(new Uri("http://foo.bar/baz/123?a=ab"));
sp.ConnectionLeaseTimeout = 60*1000; // 1 minute

第二部分我没有使用过,但如果有人想使用单例模式,请在此处发布。更详细的信息可以在下面的博客中获得,重要的部分我在这里发布,以防万一链接被破坏: http://byterot.blogspot.com/2016/07/singleton-httpclient-dns.html

我希望这会帮助像我一样被困的人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-15
    • 2021-01-25
    • 2016-05-30
    • 1970-01-01
    • 1970-01-01
    • 2017-04-18
    • 1970-01-01
    相关资源
    最近更新 更多