How to pass long string in HttpClient.PostAsync request - c#

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

Related

Post from request body with HttpClient

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.

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;

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.

Why is fiddler showing a 0 for the http post body? Does this look correct?

Trying to simply post a string to a web api to make sure it works and the json string doesn't seem to be there even though I can see it in the debugger.. Is there something obvious I am missing ?
RootObject ro = new RootObject();
ro.JobID = 9999;
var dataAsString = JsonConvert.SerializeObject(ro); //there is a json string here
var content = new StringContent(dataAsString);
var client = _clientFactory.CreateClient();
client.BaseAddress = new System.Uri("http://localhost:55816");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
var response = await client.PostAsJsonAsync("/api/Jobs/", content);
{"Files":null,"JobID":9999,"ReadyForPublish":false,"ScheduledJobID":null}
With an HttpClient you can directly use the result of JsonConvert.SerializeObject(ro); as the second argument to the PostAsJsonAsync. No need to use the StringContent.

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

Categories