Reputation: 713
I have a simple web api which gets a url and sort of generates a short number as short url. I have created a VS console application where I am calling the web api via the HTTPClient
object. When I run the code the first time, it throws the following error:
Error message: One or more errors occurred.
InnerException: System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult) at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult) --- End of inner exception stack trace --- at System.Net.TlsStream.EndWrite(IAsyncResult asyncResult) at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar) --- End of inner exception stack trace --- at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar) --- End of inner exception stack trace ---
This is the code that I am using to call the web api. Calling the web api via the browser works just fine.
static void Main(string[] args)
{
string url = "https://app1/api/AddUrl?longUrl=http://google.com";
var result = GetTinyUrl(url).Result;
Console.WriteLine(result.ShortUrl);
}
protected static async Task<UrlResponse> GetUrl(string baseUrl)
{
HttpClientHandler handler = new HttpClientHandler();
handler.UseDefaultCredentials = true;
var client = new HttpClient(handler);
var response = client.GetAsync(baseUrl).Result;
if (response.IsSuccessStatusCode)
{
HttpResponseMessage response1 = response;
response.EnsureSuccessStatusCode();
var resp = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<UrlResponse>(resp);
return result;
}
return null;
}
Upvotes: 5
Views: 17810
Reputation: 21
Choosing a higher target framework in the solution properties fixed my problem.
Upvotes: 2
Reputation: 847
Just to say I spent hours looking at a similar problem when connecting to an azure service I'm writing.
Couldn't work out why postman worked fine but my code failed miserably. Similarly other URLs I tried in my code worked fine or gave expected failures.
In the end the reason for the failures was that I forgot lower the Minimum TLS Version in the TLS/SSL Settings blade!!
I think older versions of the HttpClient can only deal with TLS v1.0.
Upvotes: 12
Reputation: 246998
Mixing .Result
blocking calls with async await can cause deadlocks.
If using async await then go async all the way, and avoid creating a new instance of the HttpClient
for each call as this can lead to socked exhaustion.
static Lazy<HttpClient> http = new Lazy<HttpClient>(() => {
//Handle TLS protocols
System.Net.ServicePointManager.SecurityProtocol =
System.Net.SecurityProtocolType.Tls
| System.Net.SecurityProtocolType.Tls11
| System.Net.SecurityProtocolType.Tls12;
var handler = new HttpClientHandler() {
UseDefaultCredentials = true
};
var client = new HttpClient(handler);
return client;
});
protected static async Task<UrlResponse> GetUrl(string baseUrl) {
var client = http.Value;
var response = await client.GetAsync(baseUrl); //Remove .Result and just await
if (response.IsSuccessStatusCode) {
HttpResponseMessage response1 = response;
response.EnsureSuccessStatusCode();
var resp = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<UrlResponse>(resp);
return result;
}
return null;
}
Since this is a console application and there is only one thread then calling the async then you have to change how you call the async method in main
static void Main(string[] args) {
string url = "https://app1/api/AddUrl?longUrl=http://google.com";
var result = GetTinyUrl(url).GetAwaiter().GetResult(); //<--
Console.WriteLine(result.ShortUrl);
}
Reference Async/Await - Best Practices in Asynchronous Programming
Upvotes: 5
Reputation: 12848
I usually don't advise using HttpClient
for web requests it sometimes likes to misbehave with various web APIs. I would highly encourage you to use HttpWebRequest
instead. You can properly craft the HTTP request with full control and will produce a better overall result.
string uri = "https://api.foo.com/bar";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(uri);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "GET"; // PUT, POST, DELETE, etc
//httpWebRequest.ContentLength = 0; // sometimes you need to send zero (depending on API)
HttpWebResponse httpResponse = (HttpWebResponse)await httpWebRequest.GetResponseAsync();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = streamReader.ReadToEnd();
// this is your code here...
var result = JsonConvert.DeserializeObject<UrlResponse>(response);
return result;
}
Upvotes: 0