1

I do understand that HttpClient has been designed to be re-used for multiple calls and I have done extensive research on why it is ideal to create a single static instance or use Singleton for the HttpClient since after disposing there is a chance that the TCP sockets remains open due to the TCP connection lifecycle.

The below is a singleton class I created for my central library in order to keep and reuse a single instance of HttpClient. Can someone help me to convert it to use IHttpClientFactory instead?

public class HttpFunctions
{
    #region Class Members
    private HttpClientHandler Http_Client_Handler { get; set; }
    private HttpClient Http_Client { get; set; }
    #endregion


    #region Constructor
    public HttpFunctions()
    {
        InitHttpClient();
    }

    ~HttpFunctions()
    {
        Dispose();
    }
    #endregion


    #region Methods
    #region Post
    public T1 PostRequest<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, T2 payload, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, Cookie[] cookies = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        T1 response = new T1();

        try
        {
            Http_Client.DefaultRequestHeaders.Clear();

            if (authenticationToken != null)
                Http_Client.DefaultRequestHeaders.Authorization = authenticationToken;

            if (httpHeaders != null)
            {
                foreach (Tuple<string, string> httpHeader in httpHeaders)
                    Http_Client.DefaultRequestHeaders.Add(httpHeader.Item1, httpHeader.Item2);
            }

            if (cookies != null)
            {
                foreach (Cookie cookie in cookies)
                    Http_Client_Handler.CookieContainer.Add(requestUri, cookie);
            }

            string jsonPayload = JsonConvert.SerializeObject(payload);
            using (HttpContent httpContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json"))
            {
                string failedReason = string.Empty;
                bool succeeded = ProcessFunctions.InnerFunctions.PerformUsingRetriesIf_ReturnIsFalse(() =>
                {
                    bool success = false;

                    using (HttpResponseMessage httpResponse = Http_Client.PostAsync(requestUri, httpContent).Result)
                    {
                        if (httpResponse.IsSuccessStatusCode)
                        {
                            response = parseResponseMethod(httpResponse);
                            success = true;
                        }
                        else
                        {
                            failedReason = httpResponse.ReasonPhrase;
                            success = false;
                        }
                    }

                    return success;
                }, maxRetries, exponentialRetries, baseValue);

                if (!succeeded)
                    throw new Exception(failedReason);
            }
        }
        finally
        {
            if (cookies != null)
            {
                foreach (Cookie cookie in cookies)
                    Http_Client_Handler.CookieContainer.GetCookies(requestUri)
                                                        .Cast<Cookie>()
                                                        .ToList()
                                                        .ForEach(c => c.Expired = true);
            }
            Http_Client.DefaultRequestHeaders.Clear();
        }

        return response;
    }
    #endregion


    #region Get
    public T1 GetRequest<T1>(Func<HttpResponseMessage, T1> parseResponseMethod, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        T1 response = new T1();
        try
        {
            Http_Client.DefaultRequestHeaders.Clear();

            if (authenticationToken != null)
                Http_Client.DefaultRequestHeaders.Authorization = authenticationToken;

            if (httpHeaders != null)
            {
                foreach (Tuple<string, string> httpHeader in httpHeaders)
                    Http_Client.DefaultRequestHeaders.Add(httpHeader.Item1, httpHeader.Item2);
            }

            string failedReason = string.Empty;
            bool succeeded = ProcessFunctions.InnerFunctions.PerformUsingRetriesIf_ReturnIsFalse(() =>
            {
                bool success = false;

                using (HttpResponseMessage httpResponse = Http_Client.GetAsync(requestUri).Result)
                {
                    if (httpResponse.IsSuccessStatusCode)
                    {
                        response = parseResponseMethod(httpResponse);
                        success = true;
                    }
                    else
                    {
                        failedReason = httpResponse.ReasonPhrase;
                        success = false;
                    }
                }

                return success;
            }, maxRetries, exponentialRetries, baseValue);

            if (!succeeded)
                throw new Exception(failedReason);
        }
        finally { Http_Client.DefaultRequestHeaders.Clear(); }

        return response;
    }
    #endregion


    #region Parse
    public T1 ParseToResponseContract<T1>(HttpResponseMessage httpResponse) where T1 : new()
    {
        T1 results = new T1();

        try
        {
            if (httpResponse.Content.Headers.ContentType.ToString().ToLower().Contains("application/json"))
            {
                string jsonResponse = httpResponse.Content.ReadAsStringAsync().Result;

                using (MemoryStream jsonStreamResponse = new MemoryStream(Encoding.ASCII.GetBytes(jsonResponse)))
                {
                    //DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings()
                    //{
                    //    //DateTimeFormat = new DateTimeFormat("yyyy-MM-ddTHH:mm:ss"),
                    //    UseSimpleDictionaryFormat = true,
                    //    EmitTypeInformation = EmitTypeInformation.Never
                    //};

                    DataContractJsonSerializer dcs = new DataContractJsonSerializer(typeof(T1));
                    results = (T1)dcs.ReadObject(jsonStreamResponse);
                }
            }
        }
        finally { }

        return results;
    }
    #endregion
    #endregion


    #region Private Methods
    private void InitHttpClient()
    {
        if (Http_Client == null)
        {
            Http_Client_Handler = new HttpClientHandler() { CookieContainer = new CookieContainer() };

            Http_Client = new HttpClient(Http_Client_Handler);

            Http_Client.DefaultRequestHeaders.Accept.Clear();
        }
    }

    private void Dispose()
    {
        if (Http_Client != null)
        {
            Http_Client.Dispose();

            Http_Client = null;
        }

        if (Http_Client_Handler != null)
        {
            Http_Client_Handler.Dispose();

            Http_Client_Handler = null;
        }
    }
    #endregion


    #region Static Properties
    private static readonly IHttpFunctions mHttpFunctions = new HttpFunctions();

    public static IHttpFunctions InnerFunctions
    {
        get { return mHttpFunctions; }
    }
    #endregion
}

Anonymous
  • 27
  • 2
  • What kind of application is it? – Aluan Haddad Feb 28 '21 at 08:02
  • 2
    [Use IHttpClientFactory](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests) – Alexander Petrov Feb 28 '21 at 08:49
  • @AluanHaddad - It is a central Library project that is called by all my other apps so that I do not need to repeat and retest code that is reused across different project types. – Anonymous Mar 05 '21 at 20:36
  • @AlexanderPetrov - IHttpClientFactory needs certain configuration pertaining to the app that is using it. Since I wish to centralize this "API Calling" functionality, I do not think it is the ideal in my case. – Anonymous Mar 05 '21 at 20:36
  • Do you use .net core? – sa-es-ir Mar 08 '21 at 11:46
  • @SaeedEsmaeelinejad - yes, but for my central library I used .Net Standard in order to make it multi-platform. – Anonymous Mar 18 '21 at 07:58
  • It's not a problem just add ``Microsoft.Extensions.Http`` to library and use ``IHttpClientFactory``. – sa-es-ir Mar 18 '21 at 09:50
  • Thank you for that @SaeedEsmaeelinejad. So all i do is to add a `private IHttpClientFactory Http_Client_Factory;` as a global class variable, and then in the `InitHttpClient()` method I change the `Http_Client = new HttpClient(Http_Client_Handler);` to be `Http_Client = Http_Client_Factory.CreateClient();` and then leave the rest as is? What about the `HttpClientHandler` for when cookies need to be added to the client instance? – Anonymous Mar 18 '21 at 17:46
  • Yes, I suggest to use ``Named client type`` and then you can create custom ``HttpClientHandler`` one for use cookie and one for without cookie and in your method do ``InitHttpClient(bool useCookie)`` , see this https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-5.0 – sa-es-ir Mar 18 '21 at 19:58
  • Thank you @SaeedEsmaeelinejad. One last question pls, is this a good implementation practice or this defeats the entire concept of `IHttpClientFactory`? `using (HttpClient client = Http_Client_Factory.CreateClient()) { }` – Anonymous Mar 19 '21 at 10:25
  • 1
    Using is not work for ``HttpClient``. So don't use it. ``IHttpClientFactory`` creates pool of HttpClients and reuse it. – sa-es-ir Mar 19 '21 at 10:34

1 Answers1

0

What I think I will end up doing is to use a singleton class in my central Library project and host only one instance of an HttpClient (not static) so that for every project making use of this Library will have a separate single instance of this property. I will also implement a Destroyer in order to properly Dispose of the property instance as soon as the calling project terminates.

This however does not solve my problem when having to use an HttpClientHandler in order to be able to pass Cookies along side my Post requests. Need some more thought in this aspect.

If you think there is a better way or think that this approach will lead to problems, I would appreciate the heads up.

Thanks again :)


The below is the singleton class I created for my central library. Can you help me to convert it to use IHttpClientFactory instead? Sorry in advance regarding the overkilling overload methods.

public class HttpFunctions
{
    #region Class Members
    private HttpClientHandler Http_Client_Handler { get; set; }
    private HttpClient Http_Client { get; set; }
    #endregion


    #region Constructor
    public HttpFunctions()
    {
        InitHttpClient();
    }

    ~HttpFunctions()
    {
        Dispose();
    }
    #endregion


    #region Methods
    #region Post
    public T1 PostRequestUsingBearerAuthentication<T1, T2>(string accessToken, T2 payload, string requestUri, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequestUsingBearerAuthentication<T1, T2>(accessToken, payload, requestUri, null, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequestUsingBearerAuthentication<T1, T2>(string accessToken, T2 payload, string requestUri, Tuple<string, string>[] httpHeaders = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequestUsingBearerAuthentication<T1, T2>(accessToken, payload, requestUri, httpHeaders, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequestUsingBearerAuthentication<T1, T2>(string accessToken, T2 payload, string requestUri, Cookie[] cookies = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequestUsingBearerAuthentication<T1, T2>(accessToken, payload, requestUri, null, cookies, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequestUsingBearerAuthentication<T1, T2>(string accessToken, T2 payload, string requestUri, Tuple<string, string>[] httpHeaders = null, Cookie[] cookies = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequestUsingBearerAuthentication<T1, T2>(ParseToResponseContract<T1>, accessToken, payload, requestUri, httpHeaders, cookies, maxRetries, exponentialRetries, baseValue);
    }


    public T1 PostRequestUsingBearerAuthentication<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, string accessToken, T2 payload, string requestUri, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequestUsingBearerAuthentication<T1, T2>(parseResponseMethod, accessToken, payload, requestUri, null, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequestUsingBearerAuthentication<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, string accessToken, T2 payload, string requestUri, Tuple<string, string>[] httpHeaders = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequestUsingBearerAuthentication<T1, T2>(parseResponseMethod, accessToken, payload, requestUri, httpHeaders, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequestUsingBearerAuthentication<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, string accessToken, T2 payload, string requestUri, Cookie[] cookies = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequestUsingBearerAuthentication<T1, T2>(parseResponseMethod, accessToken, payload, requestUri, null, cookies, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequestUsingBearerAuthentication<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, string accessToken, T2 payload, string requestUri, Tuple<string, string>[] httpHeaders = null, Cookie[] cookies = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        AuthenticationHeaderValue authToken = null;

        if (!string.IsNullOrEmpty(accessToken))
            authToken = new AuthenticationHeaderValue("Bearer", accessToken);

        return PostRequest<T1, T2>(parseResponseMethod, payload, requestUri, authToken, httpHeaders, cookies, maxRetries, exponentialRetries, baseValue);
    }
   

    public T1 PostRequest<T1, T2>(T2 payload, string requestUri, AuthenticationHeaderValue authenticationToken = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(payload, requestUri, authenticationToken, null, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(T2 payload, string requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(payload, requestUri, authenticationToken, httpHeaders, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(T2 payload, string requestUri, AuthenticationHeaderValue authenticationToken = null, Cookie[] cookies = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(payload, requestUri, authenticationToken, null, cookies, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(T2 payload, string requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, Cookie[] cookies = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(ParseToResponseContract<T1>, payload, requestUri, authenticationToken, httpHeaders, cookies, maxRetries, exponentialRetries, baseValue);
    }


    public T1 PostRequest<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, T2 payload, string requestUri, AuthenticationHeaderValue authenticationToken = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(parseResponseMethod, payload, requestUri, authenticationToken, null, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, T2 payload, string requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(parseResponseMethod, payload, requestUri, authenticationToken, httpHeaders, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, T2 payload, string requestUri, AuthenticationHeaderValue authenticationToken = null, Cookie[] cookies = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(parseResponseMethod, payload, requestUri, authenticationToken, null, cookies, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, T2 payload, string requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, Cookie[] cookies = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        Uri absoluteUri = new Uri(requestUri, UriKind.Absolute);

        return PostRequest<T1, T2>(parseResponseMethod, payload, absoluteUri, authenticationToken, httpHeaders, cookies, maxRetries, exponentialRetries, baseValue);
    }



    public T1 PostRequest<T1, T2>(T2 payload, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(payload, requestUri, authenticationToken, null, null, maxRetries, exponentialRetries, baseValue);
    }
    
    public T1 PostRequest<T1, T2>(T2 payload, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(payload, requestUri, authenticationToken, httpHeaders, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(T2 payload, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Cookie[] cookies = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(payload, requestUri, authenticationToken, null, cookies, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(T2 payload, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, Cookie[] cookies = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(ParseToResponseContract<T1>, payload, requestUri, authenticationToken, httpHeaders, cookies, maxRetries, exponentialRetries, baseValue);
    }


    public T1 PostRequest<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, T2 payload, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(parseResponseMethod, payload, requestUri, authenticationToken, null, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, T2 payload, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(parseResponseMethod, payload, requestUri, authenticationToken, httpHeaders, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, T2 payload, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Cookie[] cookies = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return PostRequest<T1, T2>(parseResponseMethod, payload, requestUri, authenticationToken, null, cookies, maxRetries, exponentialRetries, baseValue);
    }

    public T1 PostRequest<T1, T2>(Func<HttpResponseMessage, T1> parseResponseMethod, T2 payload, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, Cookie[] cookies = null, short maxRetries = 5, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        T1 response = new T1();

        try
        {
            Http_Client.DefaultRequestHeaders.Clear();

            if (authenticationToken != null)
                Http_Client.DefaultRequestHeaders.Authorization = authenticationToken;

            if (httpHeaders != null)
            {
                foreach (Tuple<string, string> httpHeader in httpHeaders)
                    Http_Client.DefaultRequestHeaders.Add(httpHeader.Item1, httpHeader.Item2);
            }

            if (cookies != null)
            {
                foreach (Cookie cookie in cookies)
                    Http_Client_Handler.CookieContainer.Add(requestUri, cookie);
            }

            string jsonPayload = JsonConvert.SerializeObject(payload);
            using (HttpContent httpContent = new StringContent(jsonPayload, Encoding.UTF8, "application/json"))
            {
                string failedReason = string.Empty;
                bool succeeded = ProcessFunctions.InnerFunctions.PerformUsingRetriesIf_ReturnIsFalse(() =>
                {
                    bool success = false;

                    using (HttpResponseMessage httpResponse = Http_Client.PostAsync(requestUri, httpContent).Result)
                    {
                        if (httpResponse.IsSuccessStatusCode)
                        {
                            response = parseResponseMethod(httpResponse);
                            success = true;
                        }
                        else
                        {
                            failedReason = httpResponse.ReasonPhrase;
                            success = false;
                        }
                    }

                    return success;
                }, maxRetries, exponentialRetries, baseValue);

                if (!succeeded)
                    throw new Exception(failedReason);
            }
        }
        finally
        {
            if (cookies != null)
            {
                foreach (Cookie cookie in cookies)
                    Http_Client_Handler.CookieContainer.GetCookies(requestUri)
                                                        .Cast<Cookie>()
                                                        .ToList()
                                                        .ForEach(c => c.Expired = true);
            }
            Http_Client.DefaultRequestHeaders.Clear();
        }

        return response;
    }
    #endregion


    #region Get
    public T1 GetRequestUsingBearerAuthentication<T1>(string accessToken, string requestUri, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return GetRequestUsingBearerAuthentication<T1>(accessToken, requestUri, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 GetRequestUsingBearerAuthentication<T1>(string accessToken, string requestUri, Tuple<string, string>[] httpHeaders = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return GetRequestUsingBearerAuthentication<T1>(ParseToResponseContract<T1>, accessToken, requestUri, httpHeaders, maxRetries, exponentialRetries, baseValue);
    }

    public T1 GetRequestUsingBearerAuthentication<T1>(Func<HttpResponseMessage, T1> parseResponseMethod, string accessToken, string requestUri, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return GetRequestUsingBearerAuthentication<T1>(parseResponseMethod, accessToken, requestUri, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 GetRequestUsingBearerAuthentication<T1>(Func<HttpResponseMessage, T1> parseResponseMethod, string accessToken, string requestUri, Tuple<string, string>[] httpHeaders = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        AuthenticationHeaderValue authToken = null;

        if (!string.IsNullOrEmpty(accessToken))
            authToken = new AuthenticationHeaderValue("Bearer", accessToken);

        return GetRequest<T1>(parseResponseMethod, requestUri, authToken, httpHeaders, maxRetries, exponentialRetries, baseValue);
    }



    public T1 GetRequest<T1>(string requestUri, AuthenticationHeaderValue authenticationToken = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return GetRequest<T1>(requestUri, authenticationToken, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 GetRequest<T1>(string requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return GetRequest<T1>(ParseToResponseContract<T1>, requestUri, authenticationToken, httpHeaders, maxRetries, exponentialRetries, baseValue);
    }

    public T1 GetRequest<T1>(Func<HttpResponseMessage, T1> parseResponseMethod, string requestUri, AuthenticationHeaderValue authenticationToken = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return GetRequest<T1>(parseResponseMethod, requestUri, authenticationToken, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 GetRequest<T1>(Func<HttpResponseMessage, T1> parseResponseMethod, string requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        Uri absoluteUri = new Uri(requestUri, UriKind.Absolute);

        return GetRequest<T1>(parseResponseMethod, absoluteUri, authenticationToken, httpHeaders, maxRetries, exponentialRetries, baseValue);
    }


    public T1 GetRequest<T1>(Uri requestUri, AuthenticationHeaderValue authenticationToken = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return GetRequest<T1>(requestUri, authenticationToken, null, maxRetries, exponentialRetries, baseValue);
    }
    
    public T1 GetRequest<T1>(Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return GetRequest<T1>(ParseToResponseContract<T1>, requestUri, authenticationToken, httpHeaders, maxRetries, exponentialRetries, baseValue);
    }

    public T1 GetRequest<T1>(Func<HttpResponseMessage, T1> parseResponseMethod, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        return GetRequest<T1>(parseResponseMethod, requestUri, authenticationToken, null, maxRetries, exponentialRetries, baseValue);
    }

    public T1 GetRequest<T1>(Func<HttpResponseMessage, T1> parseResponseMethod, Uri requestUri, AuthenticationHeaderValue authenticationToken = null, Tuple<string, string>[] httpHeaders = null, short maxRetries = 0, bool exponentialRetries = false, double baseValue = 2) where T1 : new()
    {
        T1 response = new T1();
        try
        {
            Http_Client.DefaultRequestHeaders.Clear();

            if (authenticationToken != null)
                Http_Client.DefaultRequestHeaders.Authorization = authenticationToken;

            if (httpHeaders != null)
            {
                foreach (Tuple<string, string> httpHeader in httpHeaders)
                    Http_Client.DefaultRequestHeaders.Add(httpHeader.Item1, httpHeader.Item2);
            }

            string failedReason = string.Empty;
            bool succeeded = ProcessFunctions.InnerFunctions.PerformUsingRetriesIf_ReturnIsFalse(() =>
            {
                bool success = false;

                using (HttpResponseMessage httpResponse = Http_Client.GetAsync(requestUri).Result)
                {
                    if (httpResponse.IsSuccessStatusCode)
                    {
                        response = parseResponseMethod(httpResponse);
                        success = true;
                    }
                    else
                    {
                        failedReason = httpResponse.ReasonPhrase;
                        success = false;
                    }
                }

                return success;
            }, maxRetries, exponentialRetries, baseValue);

            if (!succeeded)
                throw new Exception(failedReason);
        }
        finally { Http_Client.DefaultRequestHeaders.Clear(); }

        return response;
    }
    #endregion


    #region Parse
    public T1 ParseToResponseContract<T1>(HttpResponseMessage httpResponse) where T1 : new()
    {
        T1 results = new T1();

        try
        {
            if (httpResponse.Content.Headers.ContentType.ToString().ToLower().Contains("application/json"))
            {
                string jsonResponse = httpResponse.Content.ReadAsStringAsync().Result;

                using (MemoryStream jsonStreamResponse = new MemoryStream(Encoding.ASCII.GetBytes(jsonResponse)))
                {
                    //DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings()
                    //{
                    //    //DateTimeFormat = new DateTimeFormat("yyyy-MM-ddTHH:mm:ss"),
                    //    UseSimpleDictionaryFormat = true,
                    //    EmitTypeInformation = EmitTypeInformation.Never
                    //};

                    DataContractJsonSerializer dcs = new DataContractJsonSerializer(typeof(T1));
                    results = (T1)dcs.ReadObject(jsonStreamResponse);
                }
            }
        }
        finally { }

        return results;
    }
    #endregion
    #endregion


    #region Private Methods
    private void InitHttpClient()
    {
        if (Http_Client == null)
        {
            Http_Client_Handler = new HttpClientHandler() { CookieContainer = new CookieContainer() };

            Http_Client = new HttpClient(Http_Client_Handler);

            Http_Client.DefaultRequestHeaders.Accept.Clear();
        }
    }

    private void Dispose()
    {
        if (Http_Client != null)
        {
            Http_Client.Dispose();

            Http_Client = null;
        }

        if (Http_Client_Handler != null)
        {
            Http_Client_Handler.Dispose();

            Http_Client_Handler = null;
        }
    }
    #endregion


    #region Static Properties
    private static readonly IHttpFunctions mHttpFunctions = new HttpFunctions();

    public static IHttpFunctions InnerFunctions
    {
        get { return mHttpFunctions; }
    }
    #endregion
}

Anonymous
  • 27
  • 2
  • The best choice is using ``IHttpClientFactory ``, because with singleton you don't notify with DNS changes on base address. – sa-es-ir Mar 05 '21 at 20:53
  • @SaeedEsmaeelinejad I am aware of the DNS issues related to this, but since I wish to centralize the "API Calling" functionality as much as possible in order so that I do not have to make repeated configures per application, I do not think that `IHttpClientFactory` is the ideal in this case :/ – Anonymous Mar 08 '21 at 10:30
  • You can config ``IHttpClientFactory`` in your library and just call it from another project. – sa-es-ir Mar 08 '21 at 11:13