C# HttpClient; How to make safe async calls? - c#

I have a site that exercises a third party API. This API will tell me the values of accounts for a user. The API has become unresponsive, and this has highlighted a flaw in my code. I am getting the exception "Thread was being aborted." in the try-catch trap around the call, and in EventViewer I can see the HttpException RequestTimedOut.
I am trying to use HttpClient, in a synchronous manner. This works fine when the API is responsive, but it is coming apart now that the API is very slow to respond.
I have an overview page that tries to gather all the account values for all users.
There are 3 types of users (CLIENT, LIAISON, MEMBER). The page makes three asynchronous Javascript calls for each type of account. The fact that there are three calls made is (most probably) a red-herring, but I am declaring it to be safe.
The server takes each call, gets the users for each call. So, for the CLIENT call, the system pulls up the all the CLIENT users, and serially steps through them. It makes an HTTP request per CLIENT user to get the account value.
Each call has a new HttpClient object created for it, wrapped in a using statement.
I strongly suspect that IIS is spotting the idle thread and shutting it down, which is fair enough.
How can I make the API call so that I return the thread to the pool until I get a response?
private static string getTotalAssets(string blockChainAddress, string identity)
{
var prefix = "getTotalAssets() - ";
string msg = "";
using (var handler = new HttpClientHandler { UseCookies = false, PreAuthenticate = true })
using (var client = new HttpClient(handler))
{
// Timeout
TimeSpan tsTimeout = new TimeSpan(0, 0, 0, _timeout);
client.Timeout = tsTimeout;
var message = new HttpRequestMessage(HttpMethod.Post, _urlSettleAPIGetTotalAssets);
// Accept Json
message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(Constants.MIME_JSON));
// Form the content to send
string content = getTotalAssetsContent(blockChainAddress);
// Add to the message
message.Content = new StringContent(content);
// Make API Call
string sCallRef = "GET TOTAL ASSETS FOR [" + identity + "]";
Tuple<HttpResponseMessage, string> result = MakeAPICall(client, message, content, sCallRef);
if (result.Item1 == null)
return result.Item2; // Error during call
// Call returned
HttpResponseMessage response = result.Item1;
string responseString = result.Item2;
if (!response.IsSuccessStatusCode)
{
msg = "Response Status Code: " + response.StatusCode.ToString();
logger.Debug(prefix + msg);
return "Error [" + result.Item2 + "]; " + msg;
}
// Else, call returned 200-OK
logger.Debug(prefix + "Response:\r\n" + responseString + "\r\n");
return processGetTotalAssetsResponseString(responseString);
} // end of using...
}
public static Tuple<HttpResponseMessage, string> MakeAPICall(HttpClient client, HttpRequestMessage message, string content, string callRef)
{
var prefix = "MakeAPICall() - ";
string msg = "";
HttpResponseMessage responseMessage;
string responseString = "";
try
{
responseMessage = client.SendAsync(message).GetAwaiter().GetResult();
responseString = responseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return new Tuple<HttpResponseMessage, string>(responseMessage, responseString);
}
catch (Exception ex)
{
msg = "Exception during call; \r\nBaseMessage:[" + ex.GetBaseException().Message + "]; \r\nMessage:[" + ex.Message + "]";
logger.Warn(prefix + msg);
return new Tuple<HttpResponseMessage, string>(null, msg);
}
} // end of MakeAPICall()

Related

HttpClient long polling interrupted by seperate thread

I'm running a messaging service bot, which utilities long polling to get user messages sent. This is all working fine, until I added a new component which sends a simple http get request to a heartbeat monitoring service every 30 seconds. After implementing this component, my bot code sends an additional poll request to the messaging service whenever the heartbeat component sends its request.
I'm assuming the request sent by the heartbeat component is interrupting the long polling, or something of that nature? If I comment out the heartbeat request, everything works fine.
I've tried using a shared HttpClient, and seperate HttpClients disposed after ever use.
Heartbeat monitor code:
using (var client = new HttpClient())
{
var response = await client.GetAsync(_heartbeatUrl);
var responseString = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
Log.LogMessage(response.ReasonPhrase, LogType.Error);
Log.LogMessage(responseString, LogType.Error);
}
Log.LogMessage(responseString, LogType.Verbose);
}
Message bot poll code:
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var jsonData = JsonConvert.SerializeObject(data);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
var fullString = _url + "/" + methodName;
Log.LogMessage("Querying: " + fullString, LogType.Verbose);
Log.LogMessage("With Data: " + jsonData, LogType.Verbose);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, fullString)
{
Content = content,
};
request.Headers.Authorization = new AuthenticationHeaderValue("mybot", _botToken);
var response = await client.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
returnedObject = JsonConvert.DeserializeObject<T>(responseString);
if (!response.IsSuccessStatusCode)
{
Log.LogMessage(response.ReasonPhrase, LogType.Error);
Log.LogMessage(responseString, LogType.Error);
}
Log.LogMessage(responseString, LogType.Verbose);
}
Both of these are called in simple while(true) loops, i.e
Thread th = new Thread(async a =>
{
while (true)
{
await SendHeartbeat();
Thread.Sleep(30000);
}
});
and
new Thread(async () =>
{
while (true)
{
try
{
var results = await Methods.getUpdates(_service, update_id);
} catch (Exception e) { Log.LogMessage("Error with HTML query: " + e.Message, LogType.Error, e.StackTrace); }
}
}).Start();
I'm a bit stumped as to what could be causing this. Any suggestions are much appreciated!
Figured this out in the end. My calls to my static Log class had a lock on the file write method. I believe when the heartbeat ran and logged it's result, the polling code would be blocked when trying to Log stuff, causing the thread to be released by the async await. A new thread would start polling before the previous one had finished executing.

Getting null response - azure API calling c#

Here I try to hit the API which is hosted in azure. It have to give a response as a result value - "Registered Successfully".
But the response that I got when debugging the program is "null". And also I used to call this API in a try catch block. It doesn't enter into catch block but shows a null response.
But it return valid response when I run it in POSTMAN application.
Please anyone let me know what is the problem in my code.
postman screenshot This is the postman screenshot
Here is my code
HttpClient client = new client();
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", ApiKey);
client.DefaultRequestHeaders.Add("Accept", "application/json");
string strAttn = DateTime.Now.TimeOfDay.ToString();
strAttn += "~" + personName;
strAttn += "~" + val;
string Uri = "https://orgdev.azure-api.net/APIDev/Attendance?guid=" + personID + "&timeofattendance=" + strAttn + "";
try
{
var lntResponse = await client.GetAsync(Uri);
}
catch (Exception ex)
{
string exc = ex.Message.ToString();
}

Getting "Response status code does not indicate success: 500 (Internal Server Error)" Error

At my job, we have a service that processes JSON files.
Usually, it will split a larger file into smaller files so it can multithread and process more splits faster.
Occassionally, we'll see an API Communication Failure message, and when looking into the logs, I'll see this: "Response status code does not indicate success: 500 (Internal Server Error)"
It doesn't happen all of the time, but a few times an hour. We have very high traffic, so its not even a large percentage of the files that are failing. Could someone please shed some light on what could possibly be wrong?
Here is the code where it is happening:
public class CommunicationAPI
{
static int iAPIConnectionTimeOut = int.Parse(ConfigurationManager.AppSettings["APIConnectionTimeOut"]); //15 minutes, so a timeout isn't the issue.
public static async Task<string> PostInboundActivityAsync(string sUrl, string sContent, long lSysServiceThread)
{
string sResponse = string.Empty;
try
{
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(iAPIConnectionTimeOut);
StringContent strContent = new StringContent("'" + sContent + "'", Encoding.UTF8, "application/json");
var response = client.PostAsync(sUrl, strContent);
var apiResponse = await response;
if (apiResponse != null && HttpStatusCode.PreconditionFailed == apiResponse.StatusCode)
{
return sResponse = apiResponse.Content.ReadAsStringAsync().Result;
}
else
{
apiResponse.EnsureSuccessStatusCode();
}
sResponse = apiResponse.Content.ReadAsStringAsync().Result;
// check if we received response 'OK' 200 status
if (string.IsNullOrWhiteSpace(sResponse))
{
sResponse = apiResponse.ReasonPhrase;
}
}
}
catch (Exception ex)
{
LogManager.WriteToThreadLog("Exception in API Call :" + ex.Message, lSysServiceThread);
sResponse = "EXCEPTION";
}
return sResponse;
}
}
Thank you!

500 error when making API token request to cherwell

I'm getting a 500 internal server error when trying to access cherwell REST api via Code trying to return a token to make other calls. I've verified that all the information (username, password, ClientID, and client secret) are all correct. I've also verified the server is up and accepting requests. Something must be wrong with my code. Any help would be great!
string token = "";
string responseBody;
string serverName = "ql1cwbeta1";
//initialize web client
using (WebClient webClient = new WebClient())
{
// pull down parameters for body
string grantType = ConfigurationManager.AppSettings["grant_type"];
string clientId = ConfigurationManager.AppSettings["client_id"];
string username = ConfigurationManager.AppSettings["username"];
string password = ConfigurationManager.AppSettings["password"];
string authMode = ConfigurationManager.AppSettings["auth_mode"];
//add parameters in headers
webClient.Headers.Add("Accept", "application/json");
// adding parameters in body
NameValueCollection values = new NameValueCollection
{
{"grant_type", grantType},
{"client_id", clientId},
{"username", username},
{"password", password},
{"auth_mode", authMode}
};
try
{
byte[] responseBytes = webClient.
UploadValues("http://" + serverName + "/CherwellAPI/token?auth_mode=" + authMode + "&api_key=" + clientId, "POST", values);
responseBody = Encoding.UTF8.GetString(responseBytes);
}
catch (Exception exception)
{
return exception;
}
Hope below code may help you.
public static string TokenRequest()
{
try
{
// Create HTTP Web Request for the token request
HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(Values.TokenURL);
byte[] data = Encoding.ASCII.GetBytes("username=" + Username + "&password=" + Password + "&client_id=" + ClientId + "&grant_type=" + GrantType);
// Set request verb POST, content type and content length (length of data)
tokenRequest.Method = "POST";
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.ContentLength = data.Length;
// Stream request data
using (Stream stream = tokenRequest.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
// Get the response and read stream to string
using (WebResponse response = tokenRequest.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(stream))
{
// responseText = sr.ReadToEnd();
return sr.ReadToEnd();
}
}
}
}
catch (WebException ex)
{
// Catch error for bad URL (404) or bad request (400) resulting from bad input (username, password, client id, grant type in token request)
if (ex.Message.Contains("400"))
{
// do something for bad request
}
else if (ex.Message.Contains("404"))
{
// do something for not found
}
else
{
// unknown error, do something
}
return null;
}
catch (Exception ex)
{
// General Exception
return null;
}
}
For future reference - You can include the client ID as part of the querystring if all parameters are included in the querystring, but if you are also sending a payload, then authmode should be the only thing in the querystring.
It will accept all of these parameters as querystrings.
However, you should know that for Cherwell specifically, if you are using LDAP auth / user accounts, you may need to URL encode your username values.
There are certain special characters that will break the auth command. Specifically, including a backslash for a domain can cause issues if it's not escaped with %5C , which I believe can cause a 400 error.
Also a cause of an error can be if you've chosen an authmode that's not enabled for the browser apps in the Security Settings page of the admin console ;)

Xamarin Android client post JSON to Node JS web service

I am having trouble with sending JSON data from my Android application written in C# using Xamarin Android (MvvmCross).
The function in Android application could run with no exception; however, my web service (written in Node JS using Express) seems not detecting the post request on its endpoint. Note that the other endpoints which use get (to send the data from web service to Android app) are working perfectly.
Below is my function to post my data to my web service
public async Task<int> insertSales(IEnumerable<Models.SalesTable> newsales)
{
/*ServerDatabaseApi.insertSalesEndpoint = "http://" + ipAddress + ":" + port +
"/insertsales";*/
WebRequest request = WebRequest.CreateHttp(ServerDatabaseApi.insertSalesEndpoint);
request.Method = "POST";
request.ContentType = "application/json";
try
{
using (var streamwriter = new StreamWriter(await request.GetRequestStreamAsync()))
{
string json = JsonConvert.SerializeObject(newsales, Formatting.Indented);
streamwriter.Write(json);
streamwriter.Flush();
}
return 1;
}
catch (WebException we)
{
return 0;
}
}
When running the function above, it is always succeed (return 1; always executes). I have also tried checking the JSON serialization and it is working perfectly fine.
Below also attached the endpoint code used to serve the data.
/*endpoint for inserting a new sales to sales table*/
app.post('/insertsales', function(appReq, appRes){
console.log("Insert sales; count : "+ appReq.body.length);
sql.connect(conn).then(function(){
console.log("Insert sales; count : "+ appReq.body.length);
for (var i = 0 ; i < appReq.body.length ; i++) {
new sql.Request()
.query("insert into SalesTable " +
"values ('"+appReq.body[i].DocumentNo+"','"+appReq.body[i].DateCreated+"','"+appReq.body[i].Location+"',"+
appReq.body[i].TotalDiscountAmount+","+appReq.body[i].Total+",'"+appReq.body[i].SalesmanId+"','"+
appReq.body[i].CustomerId+"',"+appReq.body[i].Latitude+","+appReq.body[i].Longitude+")")
.catch(function(err){
console.log(err);
});
}
}).catch(function(err){
console.log(err);
});
});
I tried to trace whether it reached the endpoint or not using console.log. However, it never executes.
Could you help me to spot where I went wrong? Thanks in advance.
There's nothing in your .NET code that actually sends the WebRequest. You create the request, write some JSON to it's stream, and flush it. Here's a simple way to make the network call (untested):
public async Task<int> InsertSales(IEnumerable<Models.SalesTable> newSales)
{
var ipAddress = "";// your IP address here
var port = ""; // your port here
var endpoint = $"http://{ipAddress}:{port}/insertsalesline";
var requestString = JsonConvert.SerializeObject(newSales);
var content = new StringContent(requestString, Encoding.UTF8, "application/json");
using (var client = new HttpClient())
{
var reponse = await client.PostAsync(endpoint, content);
if (reponse.IsSuccessStatusCode)
return 1;
else
return 0;
}
}

Categories

Resources