Http Method Gets Rewritten - Post => Get - c#

This is driving me cray cray, I have the following code:
public WorkItemQueryResult GetListOfStories(string queryString, string tfsInstance, string PAT)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", $"{GetPatAsEncodedString(PAT)}");
var request = new HttpRequestMessage()
{
Content = new StringContent(JsonSerializer.Serialize(queryString)),
Method = HttpMethod.Post,
RequestUri = new Uri($"{tfsInstance}_apis/wit/wiql?api-version=5.1")
};
var task = Task.Run(() => client.SendAsync(request));
task.Wait();
if (!task.Result.IsSuccessStatusCode) return new WorkItemQueryResult();
var response = task.Result.Content.ReadAsAsync<WorkItemQueryResult>();
return response.Result;
}
Which when I debug it with a break point on Task.Run shows the following:
My issue is that after executing the method I get an error from Azure DevOps telling me that the method isn't allowed. Here's the request after task.Wait:
I've verified that the same body and auth headers work through another client (YaRC). Obviously I'm doing something wrong but I can't see it :(

I had the same problem, try using 'ConfigureAwait' and 'GetAwaiter' like bellow:
var task = Task.Run(() => client.SendAsync(request));
//task.Wait();
task.ConfigureAwait(false).GetAwaiter();

Know its been a bit but, the issue ended up being that when a 3xx redirect is returned, the Authorization header is cleared out for security. So the POST is redirecting, causing a GET to be sent without authorization, resulting in 405.
The original request URL was http and as a result were getting redirected to https. Which then caused the above to occur :)

Related

How to use Post to get data from API?

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.

GET Request return 400 bad response

It's a generic question, but I need help with my specific case.
I have a simple GET endpoint (see image) which I've tested with Postman and it works
It takes two id tokens in the header and thats it.
I've put breakpoints in the code and copied the exact instance of the ids into Postman and the request works, but executing from code, I get a 400 response
using (HttpClient client = new HttpClient())
{
var request = new HttpRequestMessage()
{
RequestUri = new Uri("https://*******.execute-api.ap-southeast-2.amazonaws.com/dev/uploads/image.jpg"),
Method = HttpMethod.Get,
};
var idToken = Application.Current.Properties["id_token"].ToString();
var accessToken = Application.Current.Properties["access_token"].ToString();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.Add("Id-Token", idToken);
request.Headers.Add("Access-Token", accessToken);
var response = await client.SendAsync(request);
}
I've tried with and without the content-type header and makes no difference. Also doesn't matter if it's present in Postman
This is a Xamarin project which is where Application.Current.Properties comes from. I'm utilising other endpoints in the application are there are no issues with accessing the tokens like this.

How to diagnose a 401 error attempting to get an OAuth2 bearer token in c# .NET Core?

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.

HTTP status code 403 received when trying to retrieve response from GetAsync

I am trying to get some information from an API using GetAsync, but I am getting a status code of 403 even after adding the OAuth authorization header with the token provided for me. Is there something else I need to do, or is my token bad?
class TestAPI
{
static void Main()
{
var client = new HttpClient();
client.BaseAddress = new Uri(BASE_ADDRESS_FOR_TESTING);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", TOKEN);
HttpResponseMessage response = await client.GetAsync("WebApi/CaseLogs/v10/Search?DateFrom=04-05-2012");
if (response.IsSuccessStatusCode)
{
result = await response.Content.ReadAsStringAsync();
}
else
{
Console.WriteLine((int) response.StatusCode); // prints "403"
}
}
}
Your HttpClient looks fine to me. I think your Token is bad.
The 403 Error also supports this theory.
403
FORBIDDEN The server understood the
request but refuses to authorize it.
A server that wishes to make public why the request has been forbidden
can describe that reason in the response payload (if any).
https://httpstatuses.com/403
You should also print your response message.
using ( HttpResponseMessage response = await client.GetAsync("WebApi/CaseLogs/v10/Search?DateFrom=04-05-2012"))
using (HttpContent content = response.Content)
{
string result = await content.ReadAsStringAsync();
Console.WriteLine(result);
}
Try this code to see if you get more information in the response content.
Hope this helps.

Performing a put or patch request with token

I've got a chunk of code:
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(String.Format("{0}:{1}", "Username", "password"))));
var method = new HttpMethod("PATCH");
var reqmsg = new HttpRequestMessage(method, uri)
{
Content = new StringContent(request, Encoding.UTF8, "application/json")
};
HttpResponseMessage response = await client.SendAsync(reqmsg);
This works fine using Basic authentication. I want to use a token though, and if I change the Authorization to use a webtoken:
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Token", WebToken);
I now get a 403 Forbidden error. If I'm doing a Post or a Get, the token works, but not for Patch or Put. I'm guessing the token is somehow being stripped off. is there a way around this?
You're getting a 403 error because the encoding is incorrect.
-facepalm-
You're using UTF8 while the default is ANCI... Doesn't make sense.

Categories