Calling Rest API in a loop - c#

Let's assume I get game orders by calling a local web site's API. (A local web site selling game codes, when a user makes a purchase on the web site, I am getting orders from this web site's API.) I will do this with a .net core console app which will check the web site's API every 10 minutes. Now assume I get 5 game orders from the web site's API. Inside of this .net core console app, I will call my API in order to complete the purchase of these 5 game orders as follows. (I am calling 3rd party Rest APIs in order to get game codes.)
Can you please check my code and give me some feedback\suggestions? Is there any place to cause a problem?
static async Task Main(string[] args)
{
// Assume I am getting game orders from the web site here
//
.....
const int numberOfProducts = webSiteGameOrders;
try
{
for (var i = 0; i < numberOfProducts; i++)
{
var product = webSiteProduct;
//DEV
var request =
(HttpWebRequest)WebRequest.Create("http://test//api/purchase");
var svcCredentials =
Convert.ToBase64String(
Encoding.ASCII.GetBytes("username" + ":" + "password"));
request.Headers.Add("Authorization", "Basic " + svcCredentials);
request.Method = "POST";
request.KeepAlive = false;
request.ContentType = "application/x-www-form-urlencoded";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("productCode", webSiteProduct),
new KeyValuePair<string, string>("quantity", "1"),
new KeyValuePair<string, string>("shopNo", "1"),
new KeyValuePair<string, string>("safeNo", "1"),
new KeyValuePair<string, string>("cashierNo", "1")
});
var formUrlEncodedContent = content;
var urlEncodedString = await formUrlEncodedContent.ReadAsStringAsync();
using (var streamWriter = new StreamWriter(await request.GetRequestStreamAsync()))
{
streamWriter.Write(urlEncodedString);
}
var httpResponse = (HttpWebResponse)(await request.GetResponseAsync());
var response = new HttpResponseMessage
{
StatusCode = httpResponse.StatusCode,
Content = new StreamContent(httpResponse.GetResponseStream()),
};
//Read response
var htmlResponse = await response.Content.ReadAsStringAsync();
var deserializedResult = JObject.Parse(htmlResponse);
Console.WriteLine((string)deserializedResult["coupons"]?[0]?["Serial"] + ":" + (string)deserializedResult["coupons"]?[0]?["Pin"]);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
throw;
}
}
UPDATE:
Here is how I end up:
public class BusinessService : IHostedService
{
private IHttpClientFactory _httpClientFactory;
private readonly IHostApplicationLifetime _applicationLifetime;
private IConfiguration _configuration;
private readonly ILogger _logger;
public BusinessService(IHttpClientFactory httpClientFactory, IHostApplicationLifetime applicationLifetime, IConfiguration configuration, ILogger<BusinessService> logger)
{
_httpClientFactory = httpClientFactory;
_applicationLifetime = applicationLifetime;
_configuration = configuration;
_logger = logger;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogCritical("Critical!");
_logger.LogInformation("Application starts: ");
await MakeRequestsToRemoteService();
//Stop Application
_applicationLifetime.StopApplication();
_logger.LogInformation("Application stops: ");
}
public async Task MakeRequestsToRemoteService()
{
try
{
HttpClient httpClient = _httpClientFactory.CreateClient("OrdersClient");
var authenticationBytes = Encoding.ASCII.GetBytes("zWedH5OUUp");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(authenticationBytes));
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await httpClient.GetAsync("https://api.test.com/orders?startDate=1612126800000");
if (response.IsSuccessStatusCode)
{
Root orders = await response.Content.ReadAsAsync<Root>();
foreach (var content in orders.Content)
{
Console.Write(content.CustomerId + " " + content.CustomerFirstName + " " +
content.CustomerLastName + " " + content.CustomerEmail + " " + content.TotalPrice +
" " + content.InvoiceAddress.Phone + " " + content.Lines[0].ProductCode + " " +
content.Lines[0].ProductName);
_logger.LogInformation("Order {ID} {CustomerId}, {Name} {LastName}, {Phone}, {ProductCode}, {ProductName}, {Price}, {OrderDate} ", content.Id, content.CustomerId, content.CustomerFirstName, content.CustomerLastName, content.InvoiceAddress.Phone, content.Lines[0].ProductCode, content.Lines[0].ProductName, content.TotalPrice, DateTimeOffset.FromUnixTimeMilliseconds(content.OrderDate).DateTime);
InsertData(content);
}
}
}
catch (Exception e)
{
_logger.LogError("Error: {Error} ", e.Message);
throw;
}
}
public void InsertData(Content content)
{
var connString = _configuration["ConnectionStrings:Development"];
using (var sqlConnection = new SqlConnection(connString))
{
sqlConnection.Open();
using (SqlCommand command = new SqlCommand())
{
command.Connection = sqlConnection;
string sql = #"insert into Orders (id, customerId, firstName, lastName, phone, productCode, productName, price, orderDate, status) values (#id, #customerId, #firstName, #lastName, #phone, #productCode, #productName, #price, #orderDate, #status)";
command.CommandText = sql;
try
{
command.Parameters.Add("id", SqlDbType.BigInt).Value = content.Id;
command.Parameters.Add("customerId", SqlDbType.Int).Value = content.CustomerId;
command.Parameters.Add("firstName", SqlDbType.VarChar).Value = content.CustomerFirstName;
command.Parameters.Add("lastName", SqlDbType.VarChar).Value = content.CustomerLastName;
command.Parameters.Add("phone", SqlDbType.VarChar).Value = content.InvoiceAddress.Phone;
command.Parameters.Add("productCode", SqlDbType.Int).Value = content.Lines[0].ProductCode;
command.Parameters.Add("productName", SqlDbType.VarChar).Value = content.Lines[0].ProductName;
command.Parameters.Add("price", SqlDbType.Float).Value = content.TotalPrice;
command.Parameters.Add("orderDate", SqlDbType.BigInt).Value = content.OrderDate;
command.Parameters.Add("status", SqlDbType.TinyInt).Value = 1; //Retrieved
command.ExecuteNonQuery();
}
catch (SqlException exception)
{
if (exception.Number == 2627) // Cannot insert duplicate key row in object error
{
_logger.LogInformation("Duplicate: {ID} ", content.Id);
}
else
{
_logger.LogError("Error: {Error} ", exception.Message);
throw; // Throw exception if this exception is unexpected
}
}
}
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
}

Related

Middleware Log Grpc Request

I have asp.net grpc service with middleware to block bad user Ip.
I would like to log bad user grpc request, at the moment i can access only with headers, body look like not work.
public BlackIpService(RequestDelegate next, ILogger<BlackIpService> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
var currentIp = context.Connection.RemoteIpAddress.MapToIPv4().ToString();
if (BlackListIp.TryGetValue(currentIp, out var requestFailed))
{
var requestContent = new StringBuilder();
requestContent.AppendLine($"method = {context.Request.Method.ToUpper()} " + $"path = {context.Request.Path}");
foreach (var (headerKey, headerValue) in context.Request.Headers)
{
requestContent.AppendLine($"header = {headerKey} value = {headerValue}");
}
/*
context.Request.EnableBuffering();
var requestReader = new StreamReader(context.Request.Body);
var content = await requestReader.ReadToEndAsync(); //not work with GRPC
requestContent.AppendLine($"body = {content}");
*/
_logger.Log("BlackList", "Ip " + currentIp + " " + requestContent.ToString());
context.Request.Body.Position = 0;
return;
}
await _next.Invoke(context);
}

Trying to move the output of method to the output of variable

I am working on Azure Function (Http Trigger), and came across with this task.
I am trying to display the output of method (ListVendors.Run(logger)) into inside variable (responseMessage) so that the values would be carried into Http post.
public static class Function1
{
[FunctionName("HttpTrigger_1111_1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
///Calling from other method starts:
ILogger logger = Bootstrap.Logger("Program");
ListVendors.Run(logger);
///Calling from other method ends:
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
Basically, I am trying to insert the output of:
ListVendors.Run(logger);
Inside "responseMessage".
return new OkObjectResult(responseMessage);
How do I modify the code to do that?
Bottom is code for ListVendors:
public static class ListVendors
{
public static void Run(ILogger logger)
{
OnlineClient client = Bootstrap.Client(logger);
ReadByQuery query = new ReadByQuery()
{
ObjectName = "VENDOR",
PageSize = 2, // Keep the count to just 2 for the example
Fields =
{
"RECORDNO",
"VENDORID",
}
};
logger.LogInformation("Executing query to Intacct API");
Task<OnlineResponse> task = client.Execute(query);
task.Wait();
OnlineResponse response = task.Result;
Result result = response.Results[0];
try
{
dynamic json = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(result.Data));
string jsonString = json.ToString();
logger.LogDebug(
"Query successful - page 1 [ Total count={0}, Data={1} ]",
result.TotalCount,
jsonString
);
Console.WriteLine("Page 1 success! Number of vendor objects found: " + result.TotalCount + ". Number remaining: " + result.NumRemaining);
} catch (NullReferenceException e)
{
logger.LogDebug("No response in Data. {0}", e);
}
LogManager.Flush();
int i = 1;
while (result.NumRemaining > 0 && i <= 3 && !string.IsNullOrEmpty(result.ResultId))
{
i++;
ReadMore more = new ReadMore()
{
ResultId = result.ResultId
};
Task<OnlineResponse> taskMore = client.Execute(more);
taskMore.Wait();
OnlineResponse responseMore = taskMore.Result;
Result resultMore = responseMore.Results[0];
try
{
dynamic resultMoreJson =
JsonConvert.DeserializeObject(JsonConvert.SerializeObject(resultMore.Data));
string resultMoreJsonString = resultMoreJson.ToString();
logger.LogDebug(
"Read More successful - page " + i + " [ Total remaining={0}, Data={1} ]",
resultMore.NumRemaining,
resultMoreJsonString
);
Console.WriteLine("Page " + i + " success! Records remaining: " + resultMore.NumRemaining);
}
catch (NullReferenceException e)
{
logger.LogDebug("No response in Data. {0}", e);
}
finally
{
LogManager.Flush();
}
}
Console.WriteLine("Successfully read " + i + " pages");
}
}
}

HttpClient.SendAsync is throwing "A task was canceled" issue

We are making a PUT call to the service layer to update an entity information
HttpClient.SendAsync method is throwing "A task was canceled" issue even though the API call is successfully made to the backend service layer. I could see the logs from service layer.
Initially I thought it could be related to timeout, so I increased the timeout from 10 seconds to 30 seconds, still the program is waiting for 30 seconds and gets timed out with the error "A task was canceled". But the api call was successfully completed in Service Layer within even 5 seconds
Please find below my code
protected override void Execute(CodeActivityContext context)
{
string uuid = UUID.Get(context);
Console.WriteLine(uuid + ": Http Api Call - STARTED");
string endPoint = EndPoint.Get(context);
string contentType = ContentType.Get(context);
string acceptFormat = AcceptFormat.Get(context);
HttpMethod httpMethod = HttpApiMethod.Get(context);
string clientCertificatePath = ClientCertificatePath.Get(context);
string clientCertificatePassword = ClientCertificatePassword.Get(context);
double httpTimeOut = HttpTimeout.Get(context);
Dictionary<string, string> requestHeaders = RequestHeaders.Get(context);
Dictionary<string, string> pathParams = PathParams.Get(context);
Dictionary<string, string> queryParams = QueryParams.Get(context);
string requestBody = RequestBody.Get(context);
WebRequestHandler webRequestHandler = new WebRequestHandler();
webRequestHandler.MaxConnectionsPerServer = 1;
if (clientCertificatePath != null && clientCertificatePath.Trim().Length > 0)
{
X509Certificate2 x509Certificate2 = null;
if (clientCertificatePassword != null && clientCertificatePassword.Trim().Length > 0)
{
x509Certificate2 = new X509Certificate2(clientCertificatePath.Trim(),
clientCertificatePassword.Trim());
}
else
{
x509Certificate2 = new X509Certificate2(clientCertificatePath.Trim());
}
webRequestHandler.ClientCertificates.Add(x509Certificate2);
}
HttpClient httpClient = new HttpClient(webRequestHandler)
{
Timeout = TimeSpan.FromMilliseconds(httpTimeOut)
};
if (acceptFormat != null)
{
httpClient.DefaultRequestHeaders.Add("Accept", acceptFormat);
}
HttpResponseMessage httpResponseMessage = InvokeApiSync(httpClient, endPoint,
httpMethod, contentType,
requestHeaders, pathParams,
queryParams, requestBody,
uuid
);
HttpResponseMessageObject.Set(context, httpResponseMessage);
ResponseBody.Set(context, httpResponseMessage.Content.ReadAsStringAsync().Result);
StatusCode.Set(context, (int)httpResponseMessage.StatusCode);
Console.WriteLine(uuid + ": Http Api Call - ENDED");
}
private HttpResponseMessage InvokeApiSync(HttpClient httpClient,
string endPoint,
HttpMethod httpMethod,
string contentType,
Dictionary<string, string> requestHeaders,
Dictionary<string, string> pathParams,
Dictionary<string, string> queryParams,
string requestBody,
string uuid)
{
if (pathParams != null)
{
ICollection<string> keys = pathParams.Keys;
if (keys.Count > 0)
{
foreach (string key in keys)
{
endPoint = endPoint.Replace(":" + key, pathParams[key]);
}
}
}
if (queryParams != null)
{
List<string> keys = new List<string>(queryParams.Keys);
if (keys.Count > 0)
{
endPoint = string.Concat(endPoint, "?", keys[0], "=", queryParams[keys[0]]);
for (int index = 1; index < keys.Count; index++)
{
endPoint = string.Concat(endPoint, "&", keys[index], "=", queryParams[keys[index]]);
}
}
}
try
{
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(httpMethod, endPoint);
if (requestHeaders != null)
{
foreach (string key in requestHeaders.Keys)
{
httpRequestMessage.Headers.Add(key, requestHeaders[key]);
}
}
if (httpMethod.Equals(HttpMethod.Put) || httpMethod.Equals(HttpMethod.Post))
{
StringContent reqContent = null;
if (requestBody != null)
{
reqContent = new StringContent(requestBody);
}
else
{
reqContent = new StringContent("");
}
reqContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
httpRequestMessage.Content = reqContent;
}
HttpResponseMessage httpResponseMessage = httpClient.SendAsync(httpRequestMessage).Result;
return httpResponseMessage;
}
catch (Exception exception)
{
Console.WriteLine(uuid + " : HttpApi Error Has Occurred");
Console.WriteLine(uuid + " : " + exception.Message);
Console.WriteLine(uuid + " : " + exception.StackTrace);
throw exception;
}
}
Any help would be much appreciated. Thanks.!

how to post my data using UnityWebRequest post api call

Here is my API request
public IEnumerator Login(string bodyJsonString)
{
Debug.Log(bodyJsonString);
UnityWebRequest req = UnityWebRequest.Post("localhost:3000/login", bodyJsonString);
req.SetRequestHeader("content-type", "application/json");
yield return req.SendWebRequest();
if (req.isNetworkError || req.isHttpError)
{
Debug.Log(req.error);
}
else
{
Debug.Log("Form upload complete!");
}
}
It returns an error status code 500 and on the server returns an error Unexpected token % in JSON at position 0","severity
Here is my Coroutine Call
public void submitLogin()
{
_username = userInputField.GetComponent<InputField>().text;
_password = passwordInputField.GetComponent<InputField>().text;
Debug.Log("username" + _username);
Debug.Log("password" + _password);
string body = "{'username':'" + _username + "','password','" + _password + "'}";
//API Call
authChexi = new Auth();
StartCoroutine(authChexi.Login(body));
}
Let me know if you have ideas on how to deal with my form body. Thanks
So I have updated my function. I did some digging and finally solved it. My mistake was indeed manually building up a JSON. So here is my solution.
public void submitLogin()
{
_username = userInputField.GetComponent<InputField>().text;
_password = passwordInputField.GetComponent<InputField>().text;
//API Call
authChexi = new Auth();
StartCoroutine(authChexi.Login(_username, _password));
}
Created a class userdata for my json object
public class UserData
{
public string username;
public string password;
public string email;
}
And call the API
public IEnumerator Login(string username, string password)
{
//#TODO: call API login
// Store Token
// Add Token to headers
var user = new UserData();
user.username = username;
user.password = password;
string json = JsonUtility.ToJson(user);
var req = new UnityWebRequest("localhost:3000/login", "POST");
byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(json);
req.uploadHandler = (UploadHandler)new UploadHandlerRaw(jsonToSend);
req.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
req.SetRequestHeader("Content-Type", "application/json");
//Send the request then wait here until it returns
yield return req.SendWebRequest();
if (req.isNetworkError)
{
Debug.Log("Error While Sending: " + req.error);
}
else
{
Debug.Log("Received: " + req.downloadHandler.text);
}
}
And now it's working like a charm!
You should provide a valid JSON string in the request. You should provide double quotes instead of single quotes for each attributes with the help of the escape character ("").
Try to change the methods as follows,
public IEnumerator Login(string bodyJsonString)
{
UnityWebRequest request = new UnityWebRequest("localhost:3000/login", "POST");
byte[] data = new System.Text.UTF8Encoding().GetBytes(bodyJsonString);
request.uploadHandler = (UploadHandler) new UploadHandlerRaw(data); // important
request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json"); //important
yield return request.SendWebRequest();
if (request.isNetworkError) //
Debug.Log("Error While Sending: " + request.error);
else
Debug.Log("Received: " + request.downloadHandler.text);
}
public void submitLogin()
{
_username = userInputField.GetComponent<InputField>().text;
_password = passwordInputField.GetComponent<InputField>().text;
Debug.Log("username" + _username);
Debug.Log("password" + _password);
string body = "{\"username\":\"" + _username + "\",\"password\":\"" + _password + "\"}"; //important
//API Call
authChexi = new Auth();
StartCoroutine(authChexi.Login(body));
}

Incorrect behavior when trying to simultaneously download multiple PDFs

I have a list of reports page. I have a download button for each report. If I click 4-5 reports download button I only get the last one downloaded. Below you can see my code
public async Task<ActionResult> Download(ReportPage currentPage, string id)
{
var token = RystadIdentity.Current.AuthenticatedUser.Token;
try
{
DocumentRequestInput documentRequestInput = new DocumentRequestInput(int.Parse(id), 2);
documentRequestInput.Add(token);
}
catch
{ }
Report report = await RystadGlobal.api.GetReport(token, int.Parse(id));
string ext = Path.GetExtension(report.fileName);
byte[] byteArray = await RystadGlobal.api.DownloadReport(token, report.Id);
if (ext.ToLower() == ".pdf")
{
var name = RystadIdentity.Current.AuthenticatedUser.UserInfo.name;
var company = RystadIdentity.Current.AuthenticatedUser.UserInfo.company;
try
{
byteArray = PdfWatermarker.Watermark(byteArray, name, company);
}
catch (Exception ex)
{
//Ignore if failed and give the user the pdf anyway
}
}
return File(byteArray, System.Net.Mime.MediaTypeNames.Application.Octet, report.fileName);
}
public async Task<byte[]> DownloadReport(Token token, int reportId)
{
clientForDownloading.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token.token_type, token.access_token);
var result = await clientForDownloading.GetAsync("api/" + ApiVersion + "/LibraryDocument/" + reportId + "/download");
if (!result.IsSuccessStatusCode)
throw new ApiException("Could not download the report");
var contentBytes = await result.Content.ReadAsByteArrayAsync();
return SevenZipHelper.Decompress(contentBytes);
}
public async Task<Report> GetReport(Token token, int reportId)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token.token_type, token.access_token);
var result = await client.GetAsync("api/" + ApiVersion + "/LibraryDocument/" + reportId);
if (!result.IsSuccessStatusCode)
throw new ApiException("Could not get report");
Report report = await result.Content.ReadAsAsync<Report>();
return report;
}
Does anyone see the issue which I am missing?
You can try to do it Synchronously like so:
public ActionResult Download(ReportPage currentPage, string id)
{
var token = RystadIdentity.Current.AuthenticatedUser.Token;
try
{
DocumentRequestInput documentRequestInput = new DocumentRequestInput(int.Parse(id), 2);
documentRequestInput.Add(token);
}
catch
{ }
Report report = RystadGlobal.api.GetReport(token, int.Parse(id)).Result;
string ext = Path.GetExtension(report.fileName);
byte[] byteArray = RystadGlobal.api.DownloadReport(token, report.Id).Result;
if (ext.ToLower() == ".pdf")
{
var name = RystadIdentity.Current.AuthenticatedUser.UserInfo.name;
var company = RystadIdentity.Current.AuthenticatedUser.UserInfo.company;
try
{
byteArray = PdfWatermarker.Watermark(byteArray, name, company);
}
catch (Exception ex)
{
//Ignore if failed and give the user the pdf anyway
}
}
return File(byteArray, System.Net.Mime.MediaTypeNames.Application.Octet, report.fileName);
}
The other problem might be in this method:
RystadGlobal.api.GetReport(token, int.Parse(id))
I do not know that this method looks like inside
EDIT
Try creating new instances of your clients in the methods you added in your question:
public async Task<byte[]> DownloadReport(Token token, int reportId)
{
using(var clientForDL = new System.Net.Http.HttpClient())
{
clientForDL.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token.token_type, token.access_token);
var result = await clientForDL.GetAsync("api/" + ApiVersion + "/LibraryDocument/" + reportId + "/download");
if (!result.IsSuccessStatusCode)
throw new ApiException("Could not download the report");
var contentBytes = await result.Content.ReadAsByteArrayAsync();
return SevenZipHelper.Decompress(contentBytes);
}
}
public async Task<Report> GetReport(Token token, int reportId)
{
using(var clientForGet = new System.Net.Http.HttpClient())
{
clientForGet.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token.token_type, token.access_token);
var result = await clientForGet.GetAsync("api/" + ApiVersion + "/LibraryDocument/" + reportId);
if (!result.IsSuccessStatusCode)
throw new ApiException("Could not get report");
Report report = await result.Content.ReadAsAsync<Report>();
return report;
}
}

Categories