Simple HttpRequestMessage but not working - c#

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);

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:

Send a string in body via SendRequestAsync

I need to send a Patch request to a backend API via SendRequestAsync func. This is regarding a UWP C# app.
Backend expected to like this:
On the app this is the code I wrote. But doesn't work
if (requestMehtod == ApplicationConstants.RequestType.PATCH)
{
Uri uri = new Uri(requestUrl);
HttpRequestMessage requestMessage = null;
if (postData != null)
{
var itemAsJson = JsonConvert.SerializeObject(postData);
requestMessage = new HttpRequestMessage(HttpMethod.Patch, uri)
{
Content = new HttpStringContent(itemAsJson, Windows.Storage.Streams.UnicodeEncoding.Utf8, "application/json-patch+json")
};
}
else
{
requestMessage = new HttpRequestMessage(HttpMethod.Patch, uri);
}
response = await httpClient.SendRequestAsync(requestMessage).AsTask(cancellationTokenSource.Token);
var rdModel = ProcessResponseData(response);
return await Handle401Error(rdModel, response, postData, url, requestMehtod, isDownloadSite, OnSendRequestProgress, requestData);
}
The above code fine to send JSON data to the same API and works fine. But I need to know how to send just a string in the body. Thank for the consideration
NOTE: App uses HttpClient from Windows.Web.Http and will not be able to use anything inside System.Net.Http namespace.
The answer is given by the #gusman and #Simon Wilson. Just to amend to their answer, in order to be able to send a string in the request body, the string needs to be within double-quotes.
var requestData = "\"hello world\"";
var request = new HttpRequestMessage(HttpMethod.Post, uri)
{
Content = new HttpStringContent(requestData, Windows.Storage.Streams.UnicodeEncoding.Utf8, "application/json")
};
response = await httpClient.SendRequestAsync(request);
This worked in my scenario.

Need help converting my RestSharp code to use HttpClient instead

Due to the fact that I need to convert this C# dll into a tlp file to be called from Visual Basic 6, I need avoid using external dependencies. I have used RestSharp to consume a WebAPI by doing the following (working):
using RestSharp;
using Newtonsoft.Json;
..
public string GetToken (string Key, string Password) {
var client = new RestClient (BaseUrl + "auth/GetToken");
var request = new RestRequest (Method.POST);
request.AddHeader ("cache-control", "no-cache");
request.AddHeader ("Content-Type", "application/json");
Dictionary<string, string> data = new Dictionary<string, string> {
{ "APIKey", Key },
{ "APIPassword", Password }
};
var dataJSON = JsonConvert.SerializeObject (data);
request.AddParameter ("undefined", dataJSON, ParameterType.RequestBody);
IRestResponse response = client.Execute (request);
GetTokenResults g = JsonConvert.DeserializeObject<GetTokenResults> (response.Content);
return g.Token;
}
where GetTokenResults was a struct that contained a declaration for the string Token. I want to achieve this same functionality without using RestSharp. Here is my unsuccessful attempt:
using System.Net.Http;
using System.Net.Http.Headers;
..
public async void GetToken (string Key, string Password) {
var client = new HttpClient ( );
client.DefaultRequestHeaders.Accept.Clear ( );
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue ("application/json"));
client.BaseAddress = new Uri (BaseUrl + "auth/GetToken");
Dictionary<string, string> data = new Dictionary<string, string> {
{ "APIKey", Key },
{ "APIPassword", Password }
};
var dataJSON = JsonConvert.SerializeObject (data);
var content = new StringContent (dataJSON, Encoding.UTF8, "application/json");
var response = await client.PostAsync ("", content);
}
I am unclear on how to achieve the same results (send API key and password, return token as string) using HttpClient as I did using RestSharp earlier. Anything that can point me in the right direction is greatly appreciated!
I think you got stung by this issue. In short, the URI in client.BaseAddress needs a slash at the end of it.
However, I wouldn't simply add it, I'd consider doing it a little different. Presumably your BaseUrl already has a trailing slash, given you're appending "auth/GetToken" to it. I'd do it this way:
client.BaseAddress = new Uri(BaseUrl);
...
var response = await client.PostAsync("auth/GetToken", content);
As you can see, HttpClient fits very cleanly with how your code is already set up, i.e. you have a "base" address with a trailing slash and you want to append to it for a specific call.
That should get you un-stuck to this point. The next thing you'll need to tackle is deserializing the JSON response so you can get the token out of it. It's similar to how you did it in RestSharp, except that response.Content is not a string in the HttpClient world, so you need one more step to get that:
var json = await response.Content.ReadAsStringAsync();
GetTokenResults g = JsonConvert.DeserializeObject<GetTokenResults>(json);
return g.Token;
Last thing you'll need to do to get this to compile is change the method signature to:
public async Task<string> GetTokenAsync
One final note: you are now in the async world, and that's a good thing, but you need to know how to use it correctly or you could end up with deadlocks and other mysterious bugs. In short, don't block on async code by calling .Result or .Wait() anywhere up the call stack. That's by far most common mistake people make. Use async/await all the way down.
I think you are missing first parameter in the method PostAsync i.e. requestUri=Client.BaseAddress (see my implementation below).
Try with this first, if did not work, read below. I have a little different implementation where I passed client.BaseAddress as first parameter and I am passing my content as ByteArrayContent. In my case I have to pass my content as "application/x-www-form-urlencoded" excerpt of my code:
var buffer = Encoding.UTF8.GetBytes(content);
var byteContent = new ByteArrayContent(buffer);
//as I can't send JSON, probably, you can skip as it's already JSON
byteContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
//requestUri=client.BaseAddress
await client.PostAsync(requestUri, byteContent).ConfigureAwait(false);
We have somewhat different need but I think you are pretty close. If it does not help, write me I will share my code. After reading the comment, I would like to share how I have made my HttpClient. The code is as it is:
using (var client = CreateMailClientForPOST($"{BaseUrl}/"))
{
//removed code, you can call above code as method like
var response= await client.DoThingAsAsync($"{client.BaseAddress}, content").ConfigureAwait(false);
}
protected HttpClient CreateMailClientForPOST(string resource)
{
var handler = new HttpClientHandler();
if (handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
}
var client = new HttpClient(handler)
{
BaseAddress = new Uri($"https://api.address.com/rest/{resource}")
};
return client;
}

Salesforce Rest API patch operation - Bad Request

I'm trying to invoke a PATCH operation on our organization's Salesforce API.
The url is correct (format - https://xxxx.salesforce.com/services/data/v43.0/sobjects/Classification__c/objectid?_HttpMethod=PATCH) and so is the JSON content, though you possibly can't guage that from the code below.
public async Task PatchSalesforceObjectAsync(string objectToPost, string objectid, HttpContent content)
{
SetupHttpClient();
using (_response = await _httpClient.PostAsync($"{_sfObjectPartialURL}{objectToPost}{objectid}?_HttpMethod=PATCH", content))
{
if (_response.IsSuccessStatusCode)
{
var x = _response.Content;
}
}
}
void SetupHttpClient()
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _sfAccesstoken);
_httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
_httpClient.BaseAddress = _baseURI;
}
Response - StatusCode:400, ReasonPhrase:Bad Request
I've made the exact same request through POSTMAN and it goes through fine, so I'm guessing that the issue lies with how I'm making the call in the .Net framework.
I've also tried using a HttpRequestMessage object and then call SendAsync on the HttpClient object, but that leads to the same result.
HttpRequestMessage message = new HttpRequestMessage()
{
Method = new HttpMethod("PATCH"),
RequestUri = new Uri(_baseURI, $"{_sfObjectPartialURL}{objectToPost}{objectid}"),
Content = content,
};
using (_response = await _httpClient.SendAsync(message))
{
Rookie Mistake - There was a field in the Patch that I'm not allowed to update and since I was dealing with the same object and switching between a POST and a PATCH based on whether the object already existed I was not aware of this field level restriction.

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