C# MVC - Невозможно получить JSON данные с URL-адреса сервера WebAPI с помощью webClient - PullRequest
0 голосов
/ 13 января 2020

Я пытаюсь загрузить JSON данные из WEB API. URL работает нормально, если я запрашиваю данные из любого браузера. Тот же URL не работает, если я пытаюсь получить данные с помощью webClient. Выдает следующее сообщение об ошибке:

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 из браузера, он отправляет данные.

Ответы [ 2 ]

0 голосов
/ 20 апреля 2020

Наконец я смог решить проблему. Проблема была из-за того, что сервер обновил свой SSL до TLS 1.2 с сертификатом AES128. Я изменил 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
}

Я много искал и обнаружил, что если вы используете HttpClient в использовании блока, тогда объект будет удален, но TCP-соединение останется открытым. Это не будет закрыто. Таким образом, вышеприведенный подход работал для одного запроса, а следующие последующие запросы не выполнялись из-за этой проблемы.

Итак, я следовал нижеследующему подходу, и наконец мой код теперь работает как charm.

Я инициализировал HttpClient объект как stati c в моем классе

private static HttpClient httpClient = new HttpClient();

Затем я вызвал это для своих обобщенных c классов, передал всю информацию ему и он вернул мне желаемые результаты.

Ниже приведен полный рабочий код:

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) каждый раз, когда вы его используете.

Другое решение это установить свойство ConnectionClose для DefaultRequestHeaders на вашем HttpClient:

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

Это установит для заголовка keep-alive HTTP значение false, поэтому сокет будет закрыт после одного запроса. Оказывается, это может добавить примерно 35 мс (с длинными хвостами, то есть усиливающимися выбросами) к каждому из ваших HTTP-вызовов, не позволяя вам воспользоваться преимуществами повторного использования сокета. Итак, каково решение тогда?

Теперь, чтобы исправить это, все, что нам нужно сделать, это получить объект ServicePoint для конечной точки, передав ему URL-адрес и установив ConnectionLeaseTimeout:

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

Надеюсь, это поможет кому-то, кто застрял как я.

0 голосов
/ 13 января 2020

Для поддержки соединений 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 - устаревший способ вызова веб-сервисов, вы должны предпочесть httpclient для выполнения ваших запросов.

...