Search code examples
c#postgetdotnet-httpclient

HttpClient Sequential calls connection error


I’ve got a process that POSTS an HTTP request out to a vendor obtains the session token then runs a sequential GET request. I’m getting a “Connection Error” without much output. The secondary GET returns a response if I don’t submit both in sequence, but fails as there is no session token from the first request. It seems both run individually, but fail when running after one another. I should be ok reusing the “client” which actually seems better online. I’m wondering if it has something to do with the Tasks and Awaits. Any suggestions help!

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace actIntegration {
    public class actApi {

        // Static Encoura and mail variables.
        public string username = "**********";
        public string password = "**********";
        public string email = "**********";
        public string xApiKey = "**********";
        public string sessionToken = "";
        public string organizationUid = "";

        /**
         * Converts payload object to JSON string.
         *  
         * @return  string - Response from Encoura.
         */
        public async Task<string> getActUploadFile() {
            // Create the current client instance for Http requests.
            using (HttpClient client = new HttpClient()) {
                string response = string.Empty;
                JObject responseData = new JObject();

                var payload = new {
                    userName = username,
                    password = password,
                    acceptedTerms = true
                };

                // Convert JSON object to JSON string.
                dynamic json = JsonConvert.SerializeObject(payload);

                try {
                    response = await encouraLogin(client, json);

                    // Convert JSON response string back to a JSON object to parse and search for the token. 
                    responseData = (JObject)JsonConvert.DeserializeObject(response);

                    sessionToken = responseData["sessionToken"].Value<string>();
                    organizationUid = responseData["user"]["organizations"][0]["uid"].Value<string>();
                } catch (Exception e) {
                    // Capture additional data for the sendErrorEmail() function.
                    string data = "ERROR: " + response.ToString();

                    // Send error information to email function.
                    sendErrorEmail(e, data);
                }

                try {
                    response = await getExportList(client);

                    // Convert JSON export response string back to a JSON object to parse and search for the exports download URL. 
                    responseData = (JObject)JsonConvert.DeserializeObject(response);
                } catch (Exception e) {
                    // Capture additional data for the sendErrorEmail() function.
                    string data = "ERROR: " + response.ToString();

                    // Send error information to email function.
                    sendErrorEmail(e, data);
                }

                return responseData.ToString();
            }
        }

        /** 
         * Get token via API request. 
         * See the web api documentation at https://helpcenter.encoura.org/hc/en-us/articles/360037582012-API-Documentation-for-Automating-Downloads-of-ACT-Score-Reports-
         * 
         * @param   json - JSON key/value collection string used for sending data through to the Encoura API as settings/options.
         * 
         * @return  string - Response from Encoura Authorization API POST.
         */
        public async Task<string> encouraLogin(HttpClient client, dynamic json) {
            string response = string.Empty;

            try {
                // Geth Authorization via supplied arguments. 
                response = await getAuthorization(client, json);
            } catch (Exception e) {
                // Capture additional data for the sendErrorEmail() function.
                string data = "ERROR:" + response.ToString();

                // Send error information to email function.
                sendErrorEmail(e, data);
            }

            return response;            
        }

        /** 
         * Appends the bearer authorization token to the current HttpClient request.         
         * 
         * @param   client - Current class instance used for sending and receiving HTTP request/responses.
         * @param   json - Payload fro authentication.
         *  
         * @return  N/A - Adds the authorization bearer token to the header of the current client.
         */
        public async Task<string> getAuthorization(HttpClient client, dynamic json) {
            HttpResponseMessage response = null;
            string sResponse = string.Empty;

            try {
                // Add application/json, x-api-key to the header of the current client.          
                client.DefaultRequestHeaders.Add("x-api-key", xApiKey);

                // Add application/json to the header of the current client.
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                // Convert object to HttpContent for POST.
                StringContent content = new StringContent(json, Encoding.UTF8, "application/json");

                // Post authorization information to the client.
                response = await client.PostAsync("https://api.datalab.nrccua.org/v1/login", content);
                response.EnsureSuccessStatusCode();

                // Client response.
                sResponse = await response.Content.ReadAsStringAsync();
            } catch (Exception e) {
                // Capture additional data for the sendErrorEmail() function.
                string data = "ERROR - StatusCode:" + response.StatusCode + ", ReasonPhrase: " + response.ReasonPhrase + ", Response content = " + sResponse;

                // Send error information to email function.
                sendErrorEmail(e, data);
            }

            return sResponse;
        }

        /** 
        * Get export list via API request. 
        * 
        * @return  string - Response from Encoura.
        */
        public async Task<string> getExportList(HttpClient client) {
            HttpResponseMessage response = null;
            string sResponse = string.Empty;

            try {
                // Add application/json, x-api-key, JWT and Organization to the header of the current client.          
                client.DefaultRequestHeaders.Add("x-api-key", xApiKey);
                client.DefaultRequestHeaders.Add("Authorization", "JWT " + sessionToken);
                client.DefaultRequestHeaders.Add("Organization", organizationUid);

                // Add application/json to the header of the current client.
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                // Get export information from encoura.
                UriBuilder builder = new UriBuilder("https://api.datalab.nrccua.org/v1/datacenter/exports");
                builder.Query = "productKey=score-reporter&status=NotDelivered";
                response = await client.GetAsync(builder.Uri);

                //response = await client.GetAsync("https://api.datalab.nrccua.org/v1/datacenter/exports?productKey=score-reporter&status=NotDelivered");

                response.EnsureSuccessStatusCode();

                // Client response.
                sResponse = await response.Content.ReadAsStringAsync();                    
            } catch (Exception e) {
                // Capture additional data for the sendErrorEmail() function.
                string data = "ERROR - StatusCode:" + response.StatusCode + ", ReasonPhrase: " + response.ReasonPhrase + ", Response content = " + sResponse;

                // Send error information to email function.
                sendErrorEmail(e, data);

                return data;
            }

            return sResponse;
        }

        /**
         * Error function which emails Exception information and details of the error to the [email protected] mailbox.
         *
         * @param   e - Exception information passed to the function.
         * @param   data - Additional data captured for output.
         * 
         * @return  N/A void
         */
        public void sendErrorEmail(Exception e, string additionalData = "No additoinal data supplied.") {
            System.Net.Mail.MailMessage o = new System.Net.Mail.MailMessage(
                "**********",
                "**********; **********",
                "Error - Encoura Connection",
                "Data: \n" + e.Data + "\n\n" +
                "HelpLink: \n" + e.HelpLink + "\n\n" +
                "HResult: \n" + e.HResult + "\n\n" +
                "InnerException: \n" + e.InnerException + "\n\n" +
                "Message: \n" + e.Message + "\n\n" +
                "Source: \n" + e.Source + "\n\n" +
                "StackTrace: \n" + e.StackTrace + "\n\n" +
                "TargetSite: \n" + e.TargetSite + "\n\n" +
                "Additional Data: \n" + additionalData
            );

            System.Net.Mail.SmtpClient smtpobj = new System.Net.Mail.SmtpClient("**********", 25);
            smtpobj.EnableSsl = false;
            smtpobj.Send(o);
        }
    }
}

Solution

  • After trying multiple things to separate the calls and make sequential requests for readability I've had to revert to making the requests all within the same method. I can't be sure what is/wasn't being passed from global variables to passed parameters.

    I've updated the code to the following:

    using Newtonsoft.Json;using Newtonsoft.Json.Linq;using System;using System.Net.Http;using System.Net.Http.Headers;using System.Text;using System.Threading.Tasks;namespace actIntegration {
    public class actApi {
        // Create the current client instance for Http requests.
        static HttpClient client = new HttpClient();
    
        // Static Encoura and mail variables.
        public string username = "**********";
        public string password = "**********";
        public string email = "**********";
        public string xApiKey = "**********";
    
        /**
         * Converts payload object to JSON string.
         *  
         * @return  string - Response from Encoura.
         */
        public async Task<string> getActUploadFile() {
            string response = string.Empty;
            JObject responseData = new JObject();
    
            var payload = new {
                userName = username,
                password = password,
                acceptedTerms = true
            };
    
            // Convert JSON object to JSON string.
            dynamic json = JsonConvert.SerializeObject(payload);
    
            try {
                response = await getExport(json);
            } catch (Exception e) {
                // Capture additional data for the sendErrorEmail() function.
                string data = "ERROR: " + response.ToString();
    
                // Send error information to email function.
                sendErrorEmail(e, data);
            }
    
            return response;            
        }
    
        /** 
         * Appends the bearer authorization token to the current HttpClient request.         
         * 
         * @param   client - Current class instance used for sending and receiving HTTP request/responses.
         * @param   json - Payload fro authentication.
         *  
         * @return  N/A - Adds the authorization bearer token to the header of the current client.
         */
        public async Task<string> getExport(dynamic json) {
            HttpResponseMessage response = null;
            string sResponse = string.Empty;
            JObject responseData = new JObject();
    
            try {
                /* Authorization Request */
                // Add x-api-key to the header of the current client.          
                client.DefaultRequestHeaders.Add("x-api-key", xApiKey);
    
                // Add application/json to the header of the current client.
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    
                // Convert object to HttpContent for POST.
                StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
    
                // Post authorization information to the client.
                response = await client.PostAsync("https://api.datalab.nrccua.org/v1/login", content);
    
                // Validate POST response success.
                response.EnsureSuccessStatusCode();
    
                // Client response.
                sResponse = await response.Content.ReadAsStringAsync();
    
                // Deserialize Object to get response data for sequential request.
                responseData = (JObject)JsonConvert.DeserializeObject(sResponse);
    
                /* Get Export(s) List Request */
                // Add Authorization and Organization from previous request to the header of the current client.    
                client.DefaultRequestHeaders.Add("Authorization", "JWT " + responseData["sessionToken"].Value<string>());
                client.DefaultRequestHeaders.Add("Organization", responseData["user"]["organizations"][0]["uid"].Value<string>());
    
                // Build GET request and get export information from encoura.
                UriBuilder builder = new UriBuilder("https://api.datalab.nrccua.org/v1/datacenter/exports");
                builder.Query = "productKey=score-reporter&status=NotDelivered";
                response = await client.GetAsync(builder.Uri);
    
                // Validate GET response success.
                if (response.IsSuccessStatusCode) {
                    // Client response.
                    sResponse = await response.Content.ReadAsStringAsync();
                }
            } catch (Exception e) {
                // Capture additional data for the sendErrorEmail() function.
                string data = "ERROR - StatusCode:" + response.StatusCode + ", ReasonPhrase: " + response.ReasonPhrase + ", Response content = " + sResponse;
    
                // Send error information to email function.
                sendErrorEmail(e, data);
            }
    
            return sResponse;
        }
    
        /**
         * Error function which emails Exception information and details of the error to the [email protected] mailbox.
         *
         * @param   e - Exception information passed to the function.
         * @param   data - Additional data captured for output.
         * 
         * @return  N/A void
         */
        public void sendErrorEmail(Exception e, string additionalData = "No additoinal data supplied.") {
            System.Net.Mail.MailMessage o = new System.Net.Mail.MailMessage(
                "**********",
                "**********,
                "Error - Encoura Connection",
                "Data: \n" + e.Data + "\n\n" +
                "HelpLink: \n" + e.HelpLink + "\n\n" +
                "HResult: \n" + e.HResult + "\n\n" +
                "InnerException: \n" + e.InnerException + "\n\n" +
                "Message: \n" + e.Message + "\n\n" +
                "Source: \n" + e.Source + "\n\n" +
                "StackTrace: \n" + e.StackTrace + "\n\n" +
                "TargetSite: \n" + e.TargetSite + "\n\n" +
                "Additional Data: \n" + additionalData
            );
    
            System.Net.Mail.SmtpClient smtpobj = new System.Net.Mail.SmtpClient("**********", 25);
            smtpobj.EnableSsl = false;
            smtpobj.Send(o);
        }
    }
    

    }