Search code examples
c#wcfasynchronousasync-awaithttprequest

WCF service call async function


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."}

Solution

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