Post from request body with HttpClient - c#

Trying to consume a REST api using HttpClient. Im required to pass username and password through the body. Below is my code,Its however not posting the data values;
var data = new Dictionary<string, string>
{ {"username","username"},
{"password","password"}};
var jsonData = JsonConvert.SerializeObject(data);
HttpClient client2 = new HttpClient();
var requestContent2 = new StringContent(data.ToString(), Encoding.UTF8, "application/json");
client2.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", access_token);
HttpResponseMessage response2 = await client2.PostAsync(nitaauthapi, requestContent2);
HttpContent content2 = response2.Content;
string result2 = await content2.ReadAsStringAsync();
JObject jObject2 = JObject.Parse(result2);
string NONCE = jObject2["NONCE"].Value<string>();

Here, you correctly encode your data as JSON:
var jsonData = JsonConvert.SerializeObject(data);
Afterwards, you ignore your JSON-encoded data and instead send the string "System.Collections.Generic.Dictionary`2[System.String,System.String]" (= the output of data.ToString()) to your API endpoint:
var requestContent2 = new StringContent(data.ToString(), Encoding.UTF8, "application/json");
To fix this, use jsonData in your StringContent instead of data.ToString().
How to avoid such issues in the future: Pay attention to Visual Studio's hints. Visual Studio will highlight jsonData in a light grey color and add three dots underneath to inform you that something might be wrong with it:
Hovering over it will produce a tooltip telling you that the variable's value is unused.

Related

WPF / C# Using variables in API POST

Following on from the following question
WPF / C# Submit button to POST API
I now want to amend the POST BODY to include Username and Password Variable.
The below is what I am using at the moment
var OktaUserName = ADAccountName;
var OktaPassword = Password;
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://trial.okta.com/api/v1/authn");
request.Content = new StringContent("{\"username\":"+OktaUserName+",\"password\":"+OktaPassword+",\"options\": {\"multiOptionalFactorEnroll\": true,\"warnBeforePasswordExpired\": true}}", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
MessageBox.Show("Alert", json, "OK");
MessageBox.Show(responseContent, "Message");
I expect that the username and password is pulled from the stored variables and then added to OKTA as a new user, however with the above I get an error
ErrorCode E000003 Error Summary The Request Body was not well-formed
Any help will be greatly appreciated
Thank you #Fildor, your answers helped.
My modified request.Content is as follows
request.Content = new StringContent("{\"username\":\""+ADAccountNameStr+"\",\"password\":\""+PasswordStr+"\",\"options\": {\"multiOptionalFactorEnroll\": true,\"warnBeforePasswordExpired\": true}}", Encoding.UTF8, "application/json");

Posting JSON Array of Integers

I am attempting to POST some JSON data to an API to add accounts.
The instructions specify the ids parameter can be: a string (comma-separated), or array of integers
I realize I could put comma delimited ids into the query string however I would like to POST this data as JSON as I may have a large number of these.
Here is what I have tried:
public static HttpClient GetHttpClient()
{
var property = Properties.Settings.Default;
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(property.apiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("X-OrgSync-API-Key", property.apiKey);
return client;
}
HttpClient client = Api.GetHttpClient();
string json = "{\"ids\":[10545801,10731939]}";
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync($"{client.BaseAddress}/classifications/{classification.id}/accounts/add", httpContent);
It runs "successfully" but nothing actually gets set on the API server side.
Any ideas as to what I might be doing wrong here?
Additionally, any kind of tools/techniques etc., particularly in Visual Studio that would give me better visibility of the request/response traffic?
I know that this is possible as it correctly adds the account ids when I use a tool like Postman:
Regarding the Tools/Techniques, you can use Fiddler to capture the request and response on the fly to check if the Raw request is correct.
If you haven't used it before, have a look here for instructions on how to capture the requests and responses.
I was able to get the json string method working by changing the StringContent encoding type from Encoding.UTF8 to null OR Encoding.Default.
string json = "{\"ids\":[10545801,10731939]}";
var httpContent = new StringContent(json, Encoding.Default, "application/json");
var response = await client.PostAsync($"{client.BaseAddress}/classifications/{classification.id}/accounts/add", httpContent);
I also figured out a way to use an object containing an int array of ids with the Encoding.UTF8;
HttpClient client = Api.GetHttpClient();
var postData = new PostData {ids = new[] {10545801,10731939}};
var json = JsonConvert.SerializeObject(postData);
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync($"{client.BaseAddress}/classifications/{classification.id}/accounts/add", httpContent);
If you don't want to go to the trouble of creating a class just to store post data you can use an anonymous type:
var postData = new { ids = new[] {10545801,10731939}};
var json = JsonConvert.SerializeObject(postData);
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync($"{client.BaseAddress}/classifications/{classification.id}/accounts/add", httpContent);
Try below code it will work
using (var client= new HttpClient()) {
string json = "{\"ids\":[10545801,10731939]}";
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync($"{client.BaseAddress}/classifications/{classification.id}/accounts/add", httpContent);
// If the response contains content we want to read it!
if (response .Content != null) {
var responseContent = await response.Content.ReadAsStringAsync();
//you will get your response in responseContent
}

SharePoint 2013 REST API: Updating File Metadata

I am attempting to modify the metadata of a file that has just been uploaded.
using (HttpClient client = new HttpClient(new HttpClientHandler { Credentials = _authenticator.Credential }))
{
client.DefaultRequestHeaders.Add("X-HTTP-Method", "MERGE");
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
client.DefaultRequestHeaders.Add("X-RequestDigest", _authenticator.Token);
client.BaseAddress = _authenticator.Endpoint;
client.DefaultRequestHeaders.Add("IF-MATCH", "*");
string cmd = String.Format("_api/web/lists/GetByTitle('Drop Off Library')/items({0})", file.Id);
string jsonString = JsonConvert.SerializeObject(file);
StringContent test = new StringContent(jsonString, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(cmd, test);
}
I successfully GET the file's metadata above, and store it in a SharePoint file model of my own creation. I modify one of the file's metadata fields, and then attempt to merge the deserialized object back in. This has been resulting in a 400 Bad Request error. Any ideas of why this might be happening?
The solution would be to replace the lines:
string jsonString = JsonConvert.SerializeObject(file);
StringContent test = new StringContent(jsonString, Encoding.UTF8, "application/json");
with
var test = new StringContent(JsonConvert.SerializeObject(payload));
test.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;odata=verbose");
since in your case the invalid Content-Type header is generated.
See JSON Light support in REST SharePoint API released for a list of supported formats.

HttpPost request not working when JSON string has HTML in

If this is a duplicate of any existing question, please let me know which post has a similar situation.
I am trying to call a POST API, which actually works perfectly from REST clients like POSTMAN.
When I try to call that API from C# using HttpClient, it only works if I do not use any HTML content in the request body.
Here is my code:
HttpClient client = new HttpClient();
string baseUrl = channel.DomainName;
client.BaseAddress = new Uri(baseUrl);
client.DefaultRequestHeaders
.TryAddWithoutValidation(
"Content-Type",
"application/x-www-form-urlencoded;charset=utf-8");
const string serviceUrl = "/api/create";
var jsonString = CreateApiRequestBody(model, userId, false);
var uri = new Uri(baseUrl + serviceUrl);
try
{
HttpResponseMessage response = await client.PostAsync(uri.ToString(), new StringContent(jsonString, Encoding.UTF8, "application/json"));
if (response.IsSuccessStatusCode)
{
Stream receiveStream = response.Content.ReadAsStreamAsync().Result;
StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
var str = readStream.ReadToEnd();
}
...
}
And my jsonString looks like:
{
\"user_id\":\"6\",
\"description\":\"<h2 style=\\\"font-style:italic;\\\"><u><font><font>Test Test Test </font></font></u></h2>\\n\\n<p style=\\\"font-style: italic;\\\">Hi it's a Test JOB</p>\\n\\n<p> </p>\"
}
When I use plain text in description tag, the API returns a valid response, but not with the HTML content in it.
I believe I might be missing some extra header or something else.
Any help will be greatly appreciated.
Have you tried using WebUtility.HtmlEncode() method?
Where you're setting the StringContent content, try using WebUtility.HtmlEncode(jsonString) to make it API-friendly.
Like this:
using System.Net;
HttpResponseMessage response =
await client.PostAsync(
uri.ToString(),
new StringContent(WebUtility.HtmlEncode(jsonString),
Encoding.UTF8,
"application/json"));
Don't forget to use System.Net
That will give you a safe (especially for APIs) HTML string to use in your request.
Hope this helps.

How to pass long string in HttpClient.PostAsync request

Trying to send a rather long string to a REST web api (youtrack). I get the following exception:
Invalid URI: The Uri string is too long.
My code:
var encodedMessage = HttpUtility.UrlEncode(message);
var requestUri = string.Format("{0}{1}issue/{2}/execute?comment={3}", url, YoutrackRestUrl, issue.Id, encodedMessage);
var response = await httpClient.PostAsync(requestUri, null).ConfigureAwait(false);
So I took my chances with a FormUrlEncodedContent
var requestUri = string.Format("{0}{1}issue/{2}/execute", url, YoutrackRestUrl, issue.Id);
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("comment", message));
var content = new FormUrlEncodedContent(postData);
var response = await httpClient.PostAsync(requestUri, content).ConfigureAwait(false);
Which results in the exact same issue.
The string (comment) I am sending, is the changed file set of a commit into SVN. Which can be really long, so I don't really have a way to get around that. Is there a way to post content without the string length restriction?
Read the following topics, but didn't find an answer there:
.NET HttpClient. How to POST string value?
How do I set up HttpContent for my HttpClient PostAsync second parameter?
https://psycodedeveloper.wordpress.com/2014/06/30/how-to-call-httpclient-postasync-with-a-query-string/
http://forums.asp.net/t/2057125.aspx?Invalid+URI+The+Uri+string+is+too+long+HttpClient
Well the short answer to it - just put it into the Body, instead of trying to push all the data via the URL
But as the work on the ticket showed - the answer was here How to set large string inside HttpContent when using HttpClient?
The actual problem beeing in the FormUrlEncodedContent
Try this..Will be helpful for uwp..
Uri uri = new Uri("your uri string");
Windows.Web.Http.HttpClient client = new Windows.Web.Http.HttpClient();
var value1 = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string,string>>
{
// your key value pairs
};
var response = await client.PostAsync(uri,new HttpFormUrlEncodedContent(value1));
var result = await response.Content.ReadAsStringAsync();

Categories