How to call an API through POST method - c#

I want to write a code in C# to call an API and pass necessary parameters through POST method in order to send an OTP to the given mobile number and return the response of the request.
API Base URL (API to call): https:/api.example.com/api/sendotp.php
Use these parameters to submit the request to the above API:
Auth key : 146424AvL4aO2EHVS
Mobile no : 0123456789
OTP : 8480
Sender : TDTECH

You can use .NET's HttpClient class.
private HttpClient Http = new HttpClient();
public async void DoRequest(string authKey, string mobile, string otp, string sender)
{
var postData = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("Authkey", authKey),
new KeyValuePair<string, string>("Mobile", mobile),
new KeyValuePair<string, string>("OTP", otp),
new KeyValuePair<string, string>("Sender", sender),
});
var response = await Http.PostAsync("https://api.example.com/api/sendotp.php", postData);
var content = await response.Content.ReadAsStringAsync();
//Handle content/response.
}

Related

HttpClientFactory returns results of previous request

I have an ASP.NET Core Web API service built using .NET 6 that makes http requests using C# HttpClientFactory to external services.
The issue I am facing is that the second request with different arguments returns same result as for the previous request.
I tried clearing default headers at the start of every request no luck.
What worked for me:
RestSharp: https://restsharp.dev/
Using new HttpClient() instance instead of httpClientFactory.CreateClient()
I would like to make it work with httpClientFactory as this is the recommended way. Any thoughts why much appreciated.
// Each request has different access token but same body
public async Task<MyResponse> GetXyz(object requestBody, string accessToken)
{
var uri = "...";
return await this.httpClientFactory.CreateClient("GetXyz").PostAsync<MyResponse>(uri, requestBody, GetHeaders(accessToken));
}
private static IList<(string, string)> GetHeaders(string accessToken)
{
var headers = new List<(string, string)>
{
(HeaderNames.Accept, "application/json"),
};
if (!string.IsNullOrWhiteSpace(accessToken))
{
headers.Add((HeaderNames.Authorization, "Bearer " + accessToken));
}
return headers;
}
public static async Task<T> PostAsync<T>(this HttpClient httpClient, string uri, object data, IList<(string, string)> headers = null)
where T : class
{
// httpClient.DefaultRequestHeaders.Clear();
var body = data.Serialise(); // convert to JSON string
using (var request = new HttpRequestMessage(HttpMethod.Post, uri))
{
request.AddRequestHeaders(headers);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
using (var httpResponse = await httpClient.SendAsync(request))
{
var jsonResponse = await httpResponse.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<T>(jsonResponse);
}
}
}
EDIT 2: NETWORK TRAFFIC DIFF
Using Fiddler Classic with basic client httpclientfactory.CreateClient() here are the diffs between 2 requests headers that suffer from the issue:

HTTPClient Post Request and get User Token

I have created a function which takes the username, password and the endpoint (url) and returns the token.
Here is the function:
public string GetToken(KiaaaAuthClient authClient)
{
Debug.Log("GetToken function executed");
// HttpClient object
HttpClient client = new HttpClient();
Debug.Log("62");
// Creating values for the request
var values = new Dictionary<string, string>
{
{"username", authClient.userName},
{"password", authClient.password},
};
Debug.Log("70");
// Encoding request in Http form
var data = new FormUrlEncodedContent(values);
Debug.Log("74");
// Response message
HttpResponseMessage responseMessage = client.PostAsync(authClient.endPoint, data).Result;
Debug.Log("78");
// Json string content
var jsonContent = responseMessage.Content.ReadAsStringAsync().Result;
Debug.Log("82");
// Getting a dictrionary from the JSON string
var jsonDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonContent);
Debug.Log("86");
// if 'error' is not present in jr (dictionary)
if (!jsonDict.ContainsKey("error"))
{
authClient.token = jsonDict["access_token"];
return authClient.token;
}
else
{
Debug.Log("Error: failed to acquire token");
return null;
}
It gave the webexception error (name resolution failure). Can anyone give me the reason for this? Moreover, I am very new to this, I am not sure whether it is a correct way to post a request and get the user authentication token. Please guide!

Simple HttpRequestMessage but not working

I'm writing a simple dotnet core API, under search controller which like below :
[HttpGet("order")]
public async Task <Order> SearchOrder(string ordername, int siteid) {
return await service.getorder(ordername,siteid)
}
The swagger UI where the path https://devehost/search/order test pretty work, but when I use another client to call this api by below
client = new HttpClient {
BaseAddress = new Uri("https://devehost")
};
var request = new HttpRequestMessage(HttpMethod.Get, "Search/order") {
Content = new FormUrlEncodedContent(
new List<KeyValuePair<string, string>> {
new("ordername", "pizza-1"),
new("siteid", "1"),
})
};
var response = await client.SendAsync(request);
The status code always return bad request. But the postman is work, can I know the problem inside?
Thank you
For a GET request, the parameters should be sent in the querystring, not the request body.
GET - HTTP | MDN
Note: Sending body/payload in a GET request may cause some existing implementations to reject the request — while not prohibited by the specification, the semantics are undefined.
For .NET Core, you can use the Microsoft.AspNetCore.WebUtilities.QueryHelpers class to append the parameters to the URL:
Dictionary<string, string> parameters = new()
{
["ordername"] = "pizza-1",
["siteid"] = "1",
};
string url = QueryHelpers.AppendQueryString("Search/order", parameters);
using var request = new HttpRequestMessage(HttpMethod.Get, url);
using var response = await client.SendAsync(request);

Rewriting a Java HttpURLConnection method using C# HttpClient

I've got a working Java method that uses java.net.HttpURLConnection that I should re-implement in C# using the .NET HttpClient.
Java method:
public static String getMyThingAPIToken() throws IOException{
URL apiURL = new URL("https://myThingAPI/token");
HttpURLConnection apiConnection = (HttpURLConnection) apiURL.openConnection();
apiConnection.setRequestMethod("POST");
apiConnection.setDoOutput(true);
String apiBodyString = "myThingAPI login id and secret key";
byte[] apiBody = apiBodyString.getBytes(StandardCharsets.UTF_8);
OutputStream apiBodyStream = apiConnection.getOutputStream();
apiBodyStream.write(apiBody);
StringBuffer apiResponseBuffer;
try (BufferedReader in = new BufferedReader(new InputStreamReader(apiConnection.getInputStream()))){
String inputline;
apiResponseBuffer = new StringBuffer();
while((inputline = in.readLine()) != null) {
apiResponseBuffer.append(inputline);
}
}
}
So far, my C# looks like below, and you'll notice that this early form of my implementation does not interpret the response. Nor does it have a string return type required for the token string.
This is because when I test it, the response has:
StatusCode: 400
ReasonPhrase: 'Bad Request'
So something in my apiBody byte array or use of PostAsync must be different to what the Java method does, but I cannot work out what it could be.
public async static Task<HttpResponseMessage> getMyThingAPIToken(HttpClient client)
{
var apiURI = new Uri("https://myThingAPI/token");
string apiBodystring = "myThingAPI login id and secret key";
byte[] apiBody = System.Text.Encoding.UTF8.GetBytes(apiBodystring);
var response = await client.PostAsync(apiURI, new ByteArrayContent(apiBody));
return response;
}
The Java code doesn't specify a type which means that by default the request uses application/x-www-form-urlencoded. This is used for FORM POST requests.
The default content type for ByteArrayContent on the other hand is application/octet-stream while for StringContent it's text/plain.
FORM content is used through the FormUrlEncoodedContent class which can accept any Dictionary<string,string> as payload.
The input in the question is not in a x-www-form-urlencoded form so either it's not the real content or the API is misusing content types.
Assuming the API accepts proper x-www-form-urlencoded content, the following should work:
var data=new Dictionary<string,string>{
["login"]=....,
["secret"]=.....,
["someOtherField"]=....
};
var content= new FormUrlEncodedContent(data);
var response=await client.PostAsync(apiURI,content);
To send any text using application/x-www-form-urlencoded, we need to specify the content type in StringContent's constructor:
var contentType="application/x-www-form-urlencoded";
var content= new StringContent(apiBodyString, Encoding.UTF8,contentType);
var response=await client.PostAsync(apiURI,content);
Can you try using following code:
client.BaseAddress = new Uri("https://myThingAPI/");
var message = new HttpRequestMessage(HttpMethod.Post, "/token");
// Add your login id and secret key here with the format you want to send
message.Content = new StringContent(string.Format("userName={0}&password={1}", UserName, Password));
var result = await client.SendAsync(message);
return result;

Simple API Post with HttpClient

I'm trying to get a balance from coinfloor using their API,
while I was able to successfully parse their unauthenticated/public GET responses, I'm having troubles sending authenticated private POST requests with extra parameters.
Currently I get StatusCode: 401, ReasonPhrase: 'Unauthorized'
class Program
{
static HttpClient client = new HttpClient();
static void Main()
{
RunAsync().Wait();
}
static async Task RunAsync()
{
client.BaseAddress = new Uri("https://webapi.coinfloor.co.uk:8090/bist/XBT/GBP/balance/");
client.DefaultRequestHeaders.Authorization= new AuthenticationHeaderValue("Basic", Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "userID/apikey", "passphrase"))));
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("User ID","userid"),
new KeyValuePair<string, string>("API key","apikey"),
new KeyValuePair<string, string>("Passphrase","pass")
});
try
{
var result = await client.PostAsync("https://webapi.coinfloor.co.uk:8090/bist/XBT/GBP/balance/", content);
string resultContent = await result.Content.ReadAsStringAsync();
}
I've tried using both the default authorization headers and sending the data with FormUrlEncodedContent. their documentation says " All private API calls require authentication. You need to provide 3 parameters to authenticate a request: User ID, API key,Passphrase"
not sure what I'm doing wrong?
and how should I be sending other extra parameters in the requests?
just a note to mention that the code is actually correct, there was a mistake on my part with the various ids and passwords.

Categories