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.
Related
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:
I wrote a function which consumes Web API. This function works great for GET requests. However, I need to get some other resources from API which, according to API provider's docs, requires POST method. So I've simply changed HttpMethod from Get to HttpMethod.Post. When I call the API then I get Error 400 Bad Request.
CallAPI
private static async Task<T> CallAPI<T>(string endpoint, string accessToken)
{
var request = new HttpRequestMessage(HttpMethod.Post,
new Uri(ApiUri, endpoint));
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var _httpClient = new HttpClient();
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var responseStream = await response.Content.ReadAsStreamAsync();
var responseObject = await JsonSerializer.DeserializeAsync<T>(responseStream);
return responseObject;
}
I call this API:
var result = await CallAPI<SomeDataModel>("cars/locations", accessToken);
Don't know where is a problem. It works great for GET as I said. Moreover, I don't understand why there's a need to use POST instead of GETto get data. I think it's against REST best-practices. According to provider's API docs, I don't need to attach any parameters to POST request, it's just raw endpoint. Anyways, I need to use POST here to get data.
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);
I have some limited skills in c++ and have recently moved in C# (asp.net) and azure Web services. As a PoC I'm trying to make REST calls into PayPal (which I'll need to be using professionally in 3 -6 months).
I've set up my personal PayPal account using the instructions here and I get a bearer token back using curl as described in the link. Awesome.
I'm now trying to do this from .NET Core C# and all I get is a 401 error. I've examined the request and it seems the same as the curl in terms of headers; the base64 encoded credentials I think I'm adding are the same as the ones in the verbose curl log (I examined the two base64 strings by eye) so it must be something I'm doing (or not doing) in the set up of the call. I'm looking for suggestions, pointers, or flat out laughter at the obvious mistake I've made.
I've set up what I believe to be a named client thus:
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("PayPal", c =>
{
c.BaseAddress = new Uri("https://api.sandbox.paypal.com/v1/");
c.DefaultRequestHeaders.Add("Accept", "application/json");
c.DefaultRequestHeaders.Add("Accept-Language", "en_US");
});
(with all the other stuff that comes free with VS under it omitted for brevity).
I attempt the call thus:
string clientCredString = CLIENTID + ":" + SECRET;
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
var client = _clientFactory.CreateClient("PayPal");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(clientCreds));
var messageBody = new Dictionary<string,string > ();
messageBody.Add("grant_type", "client_credientials");
var request = new HttpRequestMessage(HttpMethod.Get, "oauth2/token")
{
Content = new FormUrlEncodedContent(messageBody)
};
string token;
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
token = JsonConvert.DeserializeObject<string>(json);
}
else
{
throw new ApplicationException("Well that failed");
}
and get a 401 code for my trouble.
Suggestions for troubleshooting, better methods of doing this and laughter at my foolishness all welcomed.
Update:
I read the documentation, a couple of items stand out to me:
Requires a verb of post.
Uses FormUrlEncodedContent for client credentials.
Basic auth requires username and password (Client Id & Secret)
I believe the syntax should be:
var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "...");
request.Content = new Dictionary<string, string>() { "grant_type", "client_credentials" };
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", $"{Encoding.UTF8.GetBytes($"{id}:{secret}")}");
HttpResponseMEssage = response = await client.PostAsync(request);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
For the benefit of future readers:
It was, as suggested, an encoding problem. The line:
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
needed to be
var clientCreds = System.Text.Encoding.ASCII.GetBytes(clientCredString);
It should also be noted that this particular operation requires a POST not a GET as I was using, but once I started sending properly encoded requests the errors started to make a lot more sense.
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.
}