Get Correct Response From External API using HTTP Client - c#

I am following the developer docs of this website: Yoco Charge API.
I am using C# with Dotnet core 6 for my backend server, I can see the request does return with 201 however my api always comes back with an error 500, I would like to know how I should Alter my Code to return the correct results. Here is my code:
public async Task<string> CreatePayment(string Token)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Auth-Secret-Key", paymentSeceret);
try
{
Dictionary<string, string> values = new Dictionary<string, string>
{
{"token", Token },
{"amountInCents", "1000" },
{"currency", "ZAR" }
};
var myContent = JsonSerializer.Serialize(values);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var result = await client.PostAsync(paymentString, byteContent);
return "";
}
catch (Exception ex)
{
return "Error";
}
finally
{
client.Dispose();
}
...
}
After the request goes through, I always get the following error:
Serialization and deserialization of 'System.Action' instances are not supported. Path: $.MoveNextAction.
System.NotSupportedException: Serialization and deserialization of 'System.Action' instances are not supported. Path: $.MoveNextAction.

Related

C# - httpclient - json body - Not getting applied appropriately using httpcontent

The following is the code from where I would return a tuple of response status code and response output.
private Tuple<int, string> API_Check(string URL, string reqtype, string reqbody, string split_username, string split_pwd)
{
string responsetxt="";
HttpResponseMessage httpresult = new HttpResponseMessage();
int statuscode = 0;
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
HttpClient _httpClient = new HttpClient();
var authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(split_username+":" + split_pwd));
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString);
try
{
using (var content = new StringContent(JsonConvert.SerializeObject(reqbody)))
{
if (reqtype == "GET")
{
httpresult = _httpClient.GetAsync(URL).Result;
}
if (reqtype == "PUT")
{
httpresult = _httpClient.PutAsync(URL, content).Result;
//httpresult = _httpClient.PutAsync()
}
if (reqtype == "POST")
{
httpresult = _httpClient.PostAsync(URL, content).Result;
}
statuscode = (int)httpresult.StatusCode;
responsetxt = httpresult.Content.ReadAsStringAsync().Result;
return Tuple.Create(statuscode, responsetxt);
}
}
catch (System.Net.WebException Excptn)
{
statuscode = 401;
responsetxt = Excptn.Status.ToString();
using (var stream = Excptn.Response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
MessageBox.Show(reader.ReadToEnd());
}
}
return Tuple.Create(statuscode, responsetxt);
}
For some reason, the request body is not getting filled correctly during the call. I'm getting 401 Unauthorized as for this Post call, which is definitely not a authorization error as the response message that I receive is equivalent to empty body or invalid input json format.
When I tried to hit the same reqbody for the endpoint with Postman, I'm getting 200 with valid response. Also, the GetAsync works for a similar API which doesn't require a body.
I verified there is no issues with the username, password or the Endpoint URL.
Is there a way, I could avoid using httpcontent and use the string as it is for hitting the API through C#?
Now, I could not use HttpWebRequest due to my current .Net framework limitations.
There are many issues with your code:
Primarily, you are serializing reqbody which is already a string. It sounds like you have a JSON string already, in which case you don't need to serialize it.
Don't use .Result, it can cause a deadlock. use await instead.
Use Valuetuples instead of Tuple, which can be inefficient.
Do not set ServicePointManager.SecurityProtocol = ..., instead let the operating system choose the best security protocol.
Do not use ServicePointManager in general, as it affects all HTTP request from your app. Instead set the relevant HtppClient property, or better: use HttpRequestMessage and set it directly on the message.
You can simplify the code a bit if you use HttpRequestMessage, giving it the type of HTTP method
You are catching the wrong exception type. You should be catching HttpRequestException, from which you can get the actual StatusCode.
HttpClient by default does not throw on non-success codes. You need to handle them explicitly.
Cache the HttpClient, or you could get socket exhaustion.
Creating a new HttpResponseMessage doesn't make a huge amount of sense.
HttpClient _httpClient = new HttpClient {
DefaultRequestHeaders = {
ExpectContinue = false,
},
};
private async Task<(int, string)> API_Check(string URL, HttpMethod reqtype, string reqbody, string split_username, string split_pwd)
{
var authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(split_username + ":" + split_pwd));
try
{
using (var content = new StringContent(reqbody))
using (var request = new HttpRequestMessage(URL, reqtype))
{
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString);
if (reqtype != "GET")
message.Content = content;
using var httpresult = await _httpClient.SendAsync(URL, content);
var statuscode = (int)httpresult.StatusCode;
var responsetxt = await httpresult.Content.ReadAsStringAsync();
if (!httpresult.IsSuccessStatusCode)
MessageBox.Show(responsetxt);
return (statuscode, responsetxt);
}
}
catch (HttpRequestException ex)
{
var statuscode = ex.StatusCode ?? 0;
var responsetxt = ex.Message;
MessageBox.Show(responsetxt);
return (statuscode, responsetxt);
}
}
If you actually have an object to serialize then change the method to
private async Task<(int, string)> API_Check(string URL, HttpMethod reqtype, object reqbody, string split_username, string split_pwd)
{
....
....
using (var content = new StringContent(JsonConvert.SerializeObject(reqbody)))

PostAsync API giving Bad Gateway

I am invoking a third party POST API from my own API (again POST METHOD). The third party API is having a security key, and it is working fine on the POSTMAN tool. However, when I tries to invoke through code, I am getting error, 'Bad Gateway'. Following is the code which I tried.
public static async Task<string> GetDetailsfromThirdParty(string kszstrng)
{
string contentstring = string.Empty;
using (var client = new HttpClient())
{
string baseURL = "https://abcde.kz.in/b2/vhsearch-all";
string prms = kszstrng;// input parameters to API, in JSON Format- this is JSON String.
try
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("key", "value");
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
byte[] messageBytes = System.Text.Encoding.UTF8.GetBytes(prms);
var content = new ByteArrayContent(messageBytes);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
var response = await httpClient.PostAsync(baseURL, content).ConfigureAwait(false);
var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
contentstring = result;
}
}
catch (Exception ex)
{
string msg = ex.Message.ToString();
}
return contentstring;
}
}
I am getting error on this line:
var response = await httpClient.PostAsync(baseURL, content).ConfigureAwait(false);
While trying to execute I am getting the below error:
Not able to find out what's the issue? There is no network / Fireawall blockage. I have cross-verified with Systems Team as well.
Please suggest any issue with the code.
First of all, i recommend you to not declare the HttpClient in a using statement since this can cause a socket exhaustion (because the connections will stay open).
(see the docs for details)
Go for a static HttpClient (or use the IHttpClientFactory if you're project is .net Core).
I can't test your code since I'm not able to access this api.
But give it a try using a cleaner approach:
// static HttpClient
private static readonly HttpClient _HttpClient = new HttpClient();
// Can be used to set the baseUrl of the HttpClient from outside
public static void SetBaseUrl(Uri baseUrl)
{
_HttpClient.BaseAddress = baseUrl;
}
public static async Task<string> GetDetailsfromThirdParty(string kszstrng)
{
string contentstring = string.Empty;
string baseURL = "https://abcde.kz.in/b2/vhsearch-all";
string prms = kszstrng; // input parameters to API, in JSON Format- this is JSON String.
try
{
// Be aware of which headers you wanna clean if using the static HttpClient
_HttpClient.DefaultRequestHeaders.Accept.Clear();
_HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_HttpClient.DefaultRequestHeaders.Add("key", "value");
_HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
byte[] messageBytes = Encoding.UTF8.GetBytes(prms);
var content = new ByteArrayContent(messageBytes);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _HttpClient.PostAsync(baseURL, content).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
contentstring = result;
}
}
catch (Exception ex)
{
// your exception handling
}
return contentstring;
}
Issue resolved. While forming the object to JSON String, there was an opening and closing angle brackets ([,]). Even though this is coming automatically while converting to JSON string, this was not accepted string at the vendor end. So I removed it and works perfectly. Thanks every one for the support.

Send SMS Nexmo from Azure Function got error

I try to send the SMS from Azure Function, it show the error. But if i do in Web or console app the sms succesfully sent. The error is this:
System.TypeInitializationException:'The type initializer for 'Nexmo.Api.Configuration' threw an exception.'
MissingMethodException:
Method not found: 'Microsoft.Extensions.Configuration.IConfigurationBuilder Microsoft.Extensions.Configuration.MemoryConfigurationBuilderExtensions.AddInMemoryCollection(Microsoft.Extensions.Configuration.IConfigurationBuilder, System.Collections.Generic.IEnumerable1<System.Collections.Generic.KeyValuePair2>)'.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var client = new Nexmo.Api.Client(creds: new Credentials(Api_KEY, Api_Secret));
var results = client.SMS.Send(request: new SMS.SMSRequest()
{
from = nexmo.Sender,
text = nexmo.Msg,
to = nexmo.Receiver
});
I faced the same problem, it seems that the Nexmo.Csharp.Client package does not work correctly on Azure Functions. This could be because one of the assemblies is not compatible with Azure (not all are) or some configuration setting that is not supported on Azure Functions.
I was surprised by how many references/assemblies are installed by:
Install-Package Nexmo.Csharp.Client
So I gave up on trying to identify what is the cause. Instead a much simpler solution is to create your own HttpClient and use the REST API directly (that is probably what the weighty package does anyhow).
var client = new HttpClient();
var requestContent = new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>("api_key", "YOURKEY"),
new KeyValuePair<string, string>("api_secret", "YOURSECRET"),
new KeyValuePair<string, string>("to", "TO!!!"),
new KeyValuePair<string, string>("from", "MisterCook"),
new KeyValuePair<string, string>("text", "MisterCook Says Hi")
});
HttpResponseMessage response = await client.PostAsync(
"https://rest.nexmo.com/sms/json",
requestContent);
HttpContent responseContent = response.Content;
using (var reader = new StreamReader(await responseContent.ReadAsStreamAsync()))
{
// If you need it...
}
This will spare you from deploying the bulky Nexmo.Csharp.Client package deployment and works in Azure Functions.
I realise this is an old question, but I noticed it's still not had an answer accepted.
I've just used the latest version of the .Net SDK (5.9.0) with Functions v1, v3 and v4 using the below code and had no issues
using Vonage;
using Vonage.Request;
using Vonage.Messaging;
var credentials = Credentials.FromApiKeyAndSecret("abcd123efg", "abcdefgfgfgfgf");
var VonageClient = new VonageClient(credentials);
var response = VonageClient.SmsClient.SendAnSms(new SendSmsRequest()
{
To = "0123456789",
From = "Vonage APIs",
Text = "A text message sent using the Vonage SMS API"
});
If it works locally or in another context, then the code is correct. For Azure Functions in particular I would check that the dependencies are installed as you expect, and that the variables like API key have the values you expect (add some logging to test this) - these work differently in the serverless context. Hope that helps!
I also was able to get the new WhatsApp API running with Mister Cook's approach.
public bool Send(Message msg)
{
var task = SendAsync(msg);
task.Wait();
return task.Result;
}
public async Task<bool> SendAsync(Message msg)
{
var requestJson = CreateJson(msg);
Console.WriteLine(requestJson);
var content = new StringContent(requestJson);
content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
var client = new HttpClient();
try
{
HttpResponseMessage response = await client.PostAsync("https://messages-sandbox.nexmo.com/v0.1/messages", content);
string responseJson = await response.Content.ReadAsStringAsync();
return MessageSucceeded(responseJson);
}
catch
{
return false;
}
}
private string CreateJson(Message msg)
{
JObject request = new JObject();
JObject from = new JObject();
from.Add("type", "whatsapp");
from.Add("number", "14157386170");
request.Add("from", from);
JObject to = new JObject();
to.Add("type", "whatsapp");
to.Add("number", ConnectionData);
request.Add("to", to);
JObject message = new JObject();
JObject content = new JObject();
content.Add("type", "text");
content.Add("text", msg.Text);
message.Add("content", content);
request.Add("message", message);
return request.ToString();
}
private bool MessageSucceeded(string json)
{
var obj = JObject.Parse(json);
JToken token;
return obj.TryGetValue("message_uuid", out token);
}
The RequestJson for Sandbox Testing is:
{
"from": { "type": "whatsapp", "number": "14157386170" },
"to": { "type": "whatsapp", "number": "YOUR_WHITELIST_NUMBER" },
"message": {
"content": { "type": "text", "text": "Test succeeded. Congrats" }
}
}
If the Request succeeded, the ResponseJson will contain a message_uuid. I only check wether it is contained or not
{"message_uuid": "e6859d67-1a94-44c4-bf09-a3e3fa7f8691"}

Xamarin Forms HttpClient PostAsync does not return response

I'm trying to get API response by passing service url and json parameter. Url and Parameter passing properly to the requestAPI function, but doesn't give response from PostAsync method. Url is the API location, parameter is the Category Id. When I'm running same API in the browser, it gives correct response. But not in app.
This is requestAPI function.
public async Task<ResponseObject> requestAPI(string urlString, string jsonParameters)
{
await Task.Delay(2000); // NOTE: just to simulate a HTTP request over the wire
try
{
var json = JsonConvert.SerializeObject(jsonParameters);
HttpContent httpContent = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
if (true) // no issue here.
{
response = await client.PostAsync(urlString, httpContent);
}
else
{
response = await client.PutAsync(urlString, httpContent);
}
if (response.IsSuccessStatusCode)
{
Debug.WriteLine(#" TodoItem successfully saved.");
var returnVal = await response.Content.ReadAsStringAsync();
return new ResponseObject{
jsonString = returnVal,
isSuccess = true,
error = null
};
}
else
{
return new ResponseObject
{
jsonString = null,
isSuccess = false,
error = null
};
}
}
catch (Exception ex)
{
Debug.WriteLine(#" ERROR {0}", ex.Message);
return new ResponseObject
{
jsonString = null,
isSuccess = false,
error = null
};
}
}
Category Id comes from this method.
private async void loadBookList(string categoryId)
{
IsBusy = true;
if (CrossConnectivity.Current.IsConnected)
{
ResponseObject responseObject = await _apiService.requestAPI("http://192.168.0.35/kiyawamu-api/index.php/rest/V1/mobileappintegration/getbookdetailsbycategory", Convert.ToString(categoryId));
if (responseObject.isSuccess)
{
var jsonObject = JsonConvert.DeserializeObject<BookListJsonObject>(responseObject.jsonString);
CategoryBooks = new ObservableCollection<Book>(jsonObject.booksByCategory);
}
else
{
giveAlertForCommonError();
}
}
}
I tried following solutions, but doesn't work.
Url used as a uri var uri = new Uri(urlString);
jsonParameter also used as a string
GetAsync also used
Any assistance on this will be greatly appreciated.
If you're running it on Android and the code is in a PCL, try to add ModernHttpClient so you can use the native HttpClientHandler.
https://github.com/paulcbetts/ModernHttpClient
But I also recommend all to upgrade from PCL to .NET standard. If you do so you don't have to install a NuGet package to use HttpCliennt. And then you can select implementation of HttpClientHandler in project settings.

Getting an error issuing a post request in c#

I want to issue a post request in a c# app to https://www.inventor-s-hub.xyz:8000/v8
but I keep getting this error --> System.Net.WebException: the remote name couldn't be resolved 'www.inventor-s-hub.xyz'
I have a node.js server running on the port at that domain which works fine if you navigate to it from a browser.
This is the method that I am calling later on in the app which issues a post request:
Using System.Net.Http;
public async void PostToServer(string name)
{
using (HttpClient client = new HttpClient())
{
var values = new Dictionary<string, string>
{
{ "name", name},
{ "id", "1" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("https://www.inventor-s-hub.xyz:8000/v8", content);
}
}
I've searched the web but couldn't really find anything relevant, I don't think I am running a proxy on the machine I am issuing the request from.
From the server, I am just sending a 200 status.
Try this sample:
public static async PostToServer(string name)
{
var values = new Dictionary<string, string>
{
{ "name", name},
{ "id", "1" }
};
HttpClient client = new HttpClient();
var content = new FormUrlEncodedContent(values);
HttpResponseMessage response = await client.PostAsync(new Uri("https://www.inventor-s-hub.xyz:8000/v8"), content );
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}

Categories