简体   繁体   English

C# MVC - 无法使用 webClient 从服务器 WebAPI url 检索 JSON 数据

[英]C# MVC - Not able to retreive JSON data from Server WebAPI url using webClient

I'm trying to load JSON data from WEB API.我正在尝试从 WEB API 加载 JSON 数据。 URL is working fine if I request data from any browser.如果我从任何浏览器请求数据,URL 工作正常。 The same URL is not working if I'm trying to fetch the data using webClient.如果我尝试使用 webClient 获取数据,则相同的 URL 不起作用。 It throws below error:它抛出以下错误:

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

I found so may stack overflow similar questions and implemented TSL security protocol and header changes but still its not working.我发现可能堆栈溢出类似的问题并实现了 TSL 安全协议和标头更改,但仍然无法正常工作。

Below is my code:下面是我的代码:

 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;
        }
    }

I'm stuck here as this code works sometimes fine, sometimes not works at all.我被困在这里,因为这段代码有时工作正常,有时根本不起作用。 I'm clueless whats going wrong, but I think there is something I'm missing while requesting the data like browser request but server is catching as some fishy request and not responding.我不知道出了什么问题,但我认为在请求浏览器请求等数据时我遗漏了一些东西,但服务器正在捕获一些可疑的请求并且没有响应。 But if I browse URL from browser its sending the data.但是,如果我从浏览器浏览 URL,它会发送数据。

For supporting TLS 1.2 connections, .NET 4.7.2 should be targetted.为了支持 TLS 1.2 连接,应以 .NET 4.7.2 为目标。

When you want to make sure the application is running at a certain runtime, make sure the following section is present in your config file:如果要确保应用程序在特定运行时运行,请确保配置文件中包含以下部分:

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>

Additional remark:补充说明:

I'm not sure if this could be a problem, but webclient is a deprecated way of calling webservices, you should prefer httpclient to make your requests.我不确定这是否有问题,但 webclient 是一种不推荐使用的调用 webservices 的方式,您应该更喜欢 httpclient 来提出您的请求。

Finally I was able to fix the issue.最后我能够解决这个问题。 Issue was because of server has upgraded its SSL to TLS 1.2 with AES128 certificate.问题是因为服务器已使用 AES128 证书将其 SSL 升级到 TLS 1.2。 I changed webclient to httpclient & I avoided using block.我将 webclient 更改为 httpclient & 我避免使用块。

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

I searched a lot and found that if you are using HttpClient in using block then the object will be disposed but TCP connection will remain open.我搜索了很多,发现如果您在 using 块中使用 HttpClient ,则该对象将被释放,但 TCP 连接将保持打开状态。 It will not get closed.它不会被关闭。 So above approach was working for single request and next subsequent requests were failing because of this issue.因此,上述方法适用于单个请求,而下一个后续请求由于此问题而失败。

So I followed below approach and finally my code is working like charm now.所以我遵循了下面的方法,最后我的代码现在像魅力一样工作。

I initialized the HttpClient object as static in my class我在我的类中将 HttpClient 对象初始化为静态

private static HttpClient httpClient = new HttpClient();

Then I called this for my generic classes, passed all the info to it and its returning me the desired results.然后我为我的通用类调用它,将所有信息传递给它并返回我想要的结果。

Below is the full working code:以下是完整的工作代码:

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

I had referred below blog which has all the information related to this issue: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/我参考了下面的博客,其中包含与此问题相关的所有信息: https : //aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

2) If you are creating singleton pattern for httpClient and sharing across then it has also serious behavior. 2) 如果您正在为 httpClient 创建单例模式并共享,那么它也有严重的行为。

That is HttpClientHandler creates a connection group (named with its hashcode) and does not close the connections in the group until getting disposed.即 HttpClientHandler 创建一个连接组(以其哈希码命名)并且在被处理之前不会关闭组中的连接。 This basically means the DNS check never happens as long as a connection is open.这基本上意味着只要连接打开,DNS 检查就永远不会发生。

A naive solution would be to dispose the HttpClient (hence the HttpClientHandler) every time you use it.一个简单的解决方案是在每次使用 HttpClient(因此是 HttpClientHandler)时处理它。

Another solution is to set ConnectionClose property of DefaultRequestHeaders on your HttpClient:另一种解决方案是在您的 HttpClient 上设置 DefaultRequestHeaders 的 ConnectionClose 属性:

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

This will set the HTTP's keep-alive header to false so the socket will be closed after a single request.这会将 HTTP 的 keep-alive 标头设置为 false,以便在单个请求后关闭套接字。 It turns out this can add roughly extra 35ms (with long tails, ie amplifying outliers) to each of your HTTP calls preventing you to take advantage of benefits of re-using a socket.事实证明,这会为您的每个 HTTP 调用增加大约 35 毫秒的额外时间(带有长尾,即放大异常值),从而阻止您利用重用套接字的好处。 So what is the solution then?那么有什么解决办法呢?

Now to fix it, all we need to do is to get hold of the ServicePoint object for the endpoint by passing the URL to it and set the ConnectionLeaseTimeout:现在要修复它,我们需要做的就是通过将 URL 传递给端点并设置 ConnectionLeaseTimeout 来获取端点的 ServicePoint 对象:

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

The second part I have not used but in case if any one want to use singleton pattern then posting here.第二部分我没有使用过,但如果有人想使用单例模式,请在此处发布。 The more detail information can be obtained at below blog, important part I have posted here in case in future if link gets broken: http://byterot.blogspot.com/2016/07/singleton-httpclient-dns.html更详细的信息可以在下面的博客中获得,我在这里发布了重要的部分,以防将来链接断开: http : //byterot.blogspot.com/2016/07/singleton-httpclient-dns.html

I hope this will help someone who is stuck like me.我希望这能帮助像我一样陷入困境的人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从 WebClient 或 Url 反序列化 Json 数据并将其显示在 C# 中的标准输出上 - How do I Deserialize Json data from WebClient or Url and display it on standard output in C# 如何使用C#WebClient从JSON URL请求特定对象 - How to Request Specific Object From JSON URL with C# WebClient WebClient将图像从URL下载到服务器C# - WebClient download image from URL to server C# 如何在 C# 中使用 WebClient 将数据发布到特定 URL - How to post data to specific URL using WebClient in C# C#WebClient从带有扩展名的URL下载数据 - c# webclient download data from url with extension 使用PostAsync,HttpClient和Json从C#Metro UI Client调用MVC4 WebAPI方法 - Calling MVC4 WebAPI methods from C# Metro UI Client using PostAsync, HttpClient & Json 使用 C# 从 ASP.NET Core MVC 中的 URL 解析 JSON 数据不起作用 - Parse JSON data from URL in ASP.NET Core MVC using C# doesn't work 在 C# 中通过 WebClient 将 JSON POST 到 URL - POSTing JSON to URL via WebClient in C# 在 C# 中使用 POST 方法和使用 WebClient 的请求正文从服务器接收数据 - Receive data from server using POST method and with a request body using WebClient in C# 无法从C#中的URL保存图像-使用Webclient和DownloadFile(); - Unable to save image from URL in C# - using Webclient and DownloadFile();
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM