2015-06-04 12 views
6

Stiamo utilizzando un HttpClient per inviare json a un servizio Web riposante. In un caso, ci imbattiamo in qualcosa che ci lascia sconcertati. Usando strumenti come postino, violinista ecc., Possiamo postare su un endpoint e vedere che funziona. Quando facciamo lo stesso con HttpClient.PostAsJsonAsync, possiamo verificare nel software che stiamo postando che ha ricevuto i dati bene. Tuttavia, il nostro PostAsJsonAsync finirà sempre per scadere piuttosto che darci una risposta.HttpClient.PostAsJsonAsync non vede mai quando il post ha successo e risponde

Abbiamo collaborato con il team che ha creato il servizio che stiamo consumando, oltre ai nostri test aggiuntivi da parte nostra, e non siamo ancora riusciti a usufruire del servizio.

Ogni volta che eseguiamo un post con HttpClient, possiamo verificare che il software di destinazione che pubblichiamo abbia effettivamente i dati. Ogni volta che facciamo un post per quel software di destinazione da qualsiasi altro strumento, vediamo sempre molto rapidamente una risposta con il codice di stato di 200. Qualcosa su HttpClient non riesce ad accettare la risposta da questo particolare servizio. Qualcuno ha un'idea di cosa possiamo guardare da qui?

Ecco il codice (se è così cookie cutter che quasi non mi sento è necessario)

public string PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1) 
    { 
     using (var client = new HttpClient()) 
     { 
      if (timeoutMinutes > 0) 
      { 
       client.Timeout = new TimeSpan(0,timeoutMinutes,0); 
      } 
      var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath; 
      var response = client.PostAsJsonAsync(useUrl, o).Result; 
      if(response.StatusCode == System.Net.HttpStatusCode.OK) 
      { 
       return response.Content.ReadAsStringAsync().Result; 
      } 
      return ""; 
     } 

    } 
+0

Io in realtà sono sempre lo stesso problema esatto quando si utilizza WebClient. I dati pubblicati si riflettono nel sistema di destinazione che pubblichiamo, ma la nostra richiesta si blocca. Sto iniziando a pensare che sia qualcosa a che fare con un certificato autofirmato. Ci stiamo connettendo a un'istanza stage/test di questo servizio su https e probabilmente il certificato non ha una firma valida. Finora tuttavia, non riesco a capire come verificare se questo sia o non sia effettivamente il problema. – Danny

risposta

0

Quella riga di codice nel nostro caso ha risolto il problema. Uno sviluppatore di una squadra diversa ha offerto questo suggerimento e funziona. Devo ancora google e leggere abbastanza su di esso per offrire una spiegazione di ciò che sta affrontando.

3

C'è una ragione per cui non stai seguendo l'asincrono attendere modello? Stai chiamando un metodo asincrono, ma non lo stai aspettando. Non hai detto se il codice che chiama il tuo servizio REST era un'applicazione Windows Form o ASP.NET, ma che .Result è probably causing you issues.

Puoi ristrutturare il vostro metodo come questo:

public async Task<string> PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1) 
{ 
    using (var client = new HttpClient()) 
    { 
     if (timeoutMinutes > 0) 
     { 
      client.Timeout = new TimeSpan(0,timeoutMinutes,0); 
     } 
     var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath; 
     var response = await client.PostAsJsonAsync(useUrl, o); 
     if(response.StatusCode == System.Net.HttpStatusCode.OK) 
     { 
      return await response.Content.ReadAsStringAsync(); 
     } 
     return ""; 
    } 

} 
+0

Grazie per aver trovato il tempo di rispondere. Nel mio caso, voglio che sia una chiamata sincrona. Questo codice funziona correttamente quando viene richiamato su un server Web API. Abbiamo un secondo ambiente in cui il codice viene eseguito su quello che chiamiamo il nostro "server di attività" che è esso stesso un servizio in esecuzione su un server, e prende un elenco di attività in coda e esegue più attività nel suo pool di thread. In quell'ambiente, non otteniamo la risposta. Quindi sospetto che sia la differenza nell'ambiente. – Danny

+0

Probabilmente userò il web client perché voglio che si comporti in modo sincrono. Avevo letto che HttpClient alla fine andava a deprecare il web client, e che era ancora abbastanza funzionale per roba sincrona. Indovina che ho letto male :) – Danny

+0

@Danny - Ero un fan di [RestSharp] (http://restsharp.org/) prima dell'introduzione di HttpClient. Questa potrebbe essere un'altra opzione. –

7

questo:

var response = client.PostAsJsonAsync(useUrl, o).Result; 

si sta causando il codice a un punto morto. Questo è spesso il caso quando si blocca su API asincrone, ed è per questo che si verifica l'effetto "Non vedo alcuna risposta".

Come si sta causando un deadlock? Il fatto che si sta eseguendo questa richiesta in un ambiente che contiene un contesto di sincronizzazione, forse uno che appartiene all'interfaccia utente. Esegue la richiesta asincrona e quando arriva la risposta, continua attraverso un thread di completamento I/O che tenta di postare la continuazione su quello stesso contesto UI, che è attualmente bloccato dalla tua chiamata .Result.

Se si desidera effettuare una richiesta HTTP in modo sincrono, utilizzare invece WebClient. Se si desidera sfruttare l'API asincrona correttamente, quindi await anziché il blocco con .Result.

+0

Grazie per la risposta. Quando stavo guardando al client web vs client http, ho letto che Http Client era più recente e mi è stata data la netta impressione che fosse il futuro in cui il client web sarebbe stato deprecato. Ho anche visto cose che dicevano che fare il .Result come faceva il mio codice era un modo accettabile di usare HttpClient in un modo di blocco. Daremo un'altra occhiata al client web. Grazie. – Danny

+0

@Danny A volte le persone non capiscono che stanno utilizzando le API asincrone. Puoi anche andare "raw" con 'HttpWebRequest'. –

+0

Webclient sta facendo la stessa cosa. Penso che potrebbe essere il codice .net a non piacere un certificato autofirmato che si trova sull'istanza di test/staging del servizio che stiamo usando. Avere una bestia di tempo a capire come eliminare questa possibilità, o risolverlo se questo è il problema. – Danny

1

Questa è una leggera modifica alla soluzione di @Justin Helgerson. Ci sono 2 chiamate di blocco .Result nel tuo metodo; una volta che si è asincroni, è necessario correggerli entrambi.

public async Task<string> PostDataAsync(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1) 
{ 
    using (var client = new HttpClient()) 
    { 
     if (timeoutMinutes > 0) 
     { 
      client.Timeout = new TimeSpan(0,timeoutMinutes,0); 
     } 
     var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath; 
     var response = await client.PostAsJsonAsync(useUrl, o); 
     if(response.StatusCode == System.Net.HttpStatusCode.OK) 
     { 
      return await response.Content.ReadAsStringAsync(); 
     } 
     return ""; 
    } 
} 

Nota Ho anche rinominato il metodo per PostDataAsync secondo la TAP pattern.

+0

Buona cattura, non ho visto l'altra chiamata '.Result'. –

3

Ho avuto lo stesso problema e this SO answer risolto per me.

In poche parole, è necessario utilizzare l'estensione ConfigureAwait(false) per evitare la situazione di stallo:

var response = await client.PostAsJsonAsync(useUrl, o).ConfigureAwait(false); 
+0

nota che se segui questa strada per evitare di catturare il tuo contesto di interfaccia utente, devi avere 'ConfigureAwait (false)' su * tutto * prima/sotto la tua chiamata '.Result'. Per questo motivo, è visto come un po 'fragile. – mmcrae