0

I have an async function which is used to send request message to a server. The function is like following:

class http
{
    public async Task<string> HttpRequest()
    {
        HttpRequestMessage request = GetHttpRequestMessage();
        var str1 = await ExecuteRequest(request);
        return str1;
    }

    private async Task<string> ExecuteRequest(HttpRequestMessage request)
    {
        string result = string.Empty;
        try
        {
            using (HttpClient client = new HttpClient())
            {
                var responses = await client.SendAsync(request);
                responses.EnsureSuccessStatusCode();
                result = await responses.Content.ReadAsStringAsync();
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }

        return result;
    }

    private const string _DataTypeJson = @"application/json";
    private HttpRequestMessage GetHttpRequestMessage()
    {
        Dictionary<string, string> headers = GetHeadersUsedForToken();
        string str = "https://test.com//tokens";
        Uri uri = new Uri(str);

        HttpRequestMessage request = new HttpRequestMessage
        {
            RequestUri = uri,
        };

        if (null != headers)
        {
            foreach (string key in headers.Keys)
            {
                request.Headers.Add(key, headers[key]);
            }
        }

        // Hard code Accpt type is Json
        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(_DataTypeJson));

        request.Method = HttpMethod.Get;
        return request;
    }

    private Dictionary<string, string> GetHeadersUsedForToken()
    {
        return new Dictionary<string, string>
        {
            { "id", "abc" },
            { "secret", "***" }
        };
    }
}

This function works fine in console project, but when I try to move this function to WCF service, and try to call HttpRequest() function in service,

[ServiceContract]
public interface IService1
{
    [OperationContract]
    Task<string> GetData();
}
public class Service1 : IService1
{
    public Task<string> GetData()
    {
        http test = new http();
        return test.HttpRequest();
    }       
}

Exception was thrown:

Message An error occurred while sending the request.
InnerException  {"The underlying connection was closed: An unexpected error occurred on a send."}
Kerwen
  • 516
  • 5
  • 22
  • 1
    Prematurely calling `Result` defeats the whole purpose of `async` by the way –  Mar 29 '18 at 04:46
  • 2
    And deadlocks.. – Paulo Morgado Mar 29 '18 at 06:48
  • @MickyD, Thanks your reply. The function is just a demo function which hide some details. My core problem is the function does not work in WCF. Could you help on this? – Kerwen Mar 29 '18 at 07:31
  • @PauloMorgado, thanks for your reply. Any suggestion for my problem? – Kerwen Mar 29 '18 at 07:32
  • 1
    How are we to know which code is real or not? Post your _actual code_. Good luck! –  Mar 29 '18 at 07:43
  • @MickyD sorry to make confusion. I didn't upload GetHttpRequestMessage and ConvertStreamToString function, because the problem occurs during call ExecuteRequest function. – Kerwen Mar 29 '18 at 08:00
  • async/await is about whole (lengthy) call chains. You still din't post a complete picture. Best is to create a [mcve] from scratch here. Reproduce the problem in <= 50 lines. – H H Mar 29 '18 at 14:35
  • @HenkHolterman, thanks your reply. Update my code. – Kerwen Mar 30 '18 at 03:59
  • Side note: [You're using HttpClient wrong and it is destabilizing your software](https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/) – H H Mar 30 '18 at 17:39
  • @PauloMorgado LOL! – ATL_DEV Oct 09 '18 at 22:17

1 Answers1

2

With async/await you need to make it async all the way through.

Make the service a Task-based asynchronous service

[ServiceContract]
public interface IService1 {
    [OperationContract]
    Task<string> GetData();
}

With that, it is a simple matter of making the rest of the code async all the way through.

public class http {
    public async Task<string> HttpRequestAsync() {
        var request = GetHttpRequestMessage();
        string str1 = await ExecuteRequest(request);
        Console.WriteLine(str1);
        return str1;
    }

    //...code removed for brevity as they are already Task-based
}

This should now allow the function to be used in the service implementation

public class Service1 : IService1 {
    public Task<string> GetData() {
        http test = new http(); 
        return test.HttpRequestAsync();
    }
}

In the original example provided the code was mixing async and blocking calls .Result, which can lead to deadlocks

Reference Async/Await - Best Practices in Asynchronous Programming

I would also advise making the HttpClient static and reuse it rather than creating multiple instances and disposing of them.

Reference keep an instance of HttpClient for the lifetime of your application for each distinct API that you connect to.

UPDATE:

Another possibility is that the URL being called is HTTPS.

Consider applying the following before making the request via the HttpClient

//Handle TLS protocols
System.Net.ServicePointManager.SecurityProtocol =
    System.Net.SecurityProtocolType.Tls
    | System.Net.SecurityProtocolType.Tls11
    | System.Net.SecurityProtocolType.Tls12;
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • thanks your reply. I modified my code follow your advise, but still failed in WCF service. Same exception was thrown. It will be appreciate if you can give further advise. Thanks again! – Kerwen Mar 30 '18 at 05:25
  • @Kerwen chekc to see if it is a certificate issue as you are making a call to a HTTPS, URL – Nkosi Mar 30 '18 at 05:35
  • Yes, it is! After I add SecurityProtocol, it works! Appreciate your effort, Nkosi! – Kerwen Mar 30 '18 at 05:50