Why is ResponseMessage.Content consistently empty after an await PostAsJsonAsync request? - c#

We are posting a "maintenanceEvent" to an API which consistently returns [] in ResponseMessage.Content. I'd need some expert guidance in case the code I wrote here is faulty.
private async Task SendMaintenanceEvent(object maintenanceEvent, MaintenanceEventType maintenanceEventType)
{
string endpointAddress = "TheEndpointURI";
string credentials = "OurCredentials";
string credentialsBase64 = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(credentials));
// Convert the maintenanceEvent object to consumable JSON, then encode it to a StringContent object.
this.responseInfo.MaintenanceEventAsJSON = System.Web.Helpers.Json.Encode(maintenanceEvent);
StringContent stringContent = new StringContent(this.responseInfo.MaintenanceEventAsJSON, Encoding.UTF8, "application/json");
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(endpointAddress);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentialsBase64);
this.responseInfo.AuthorizationHeader = httpClient.DefaultRequestHeaders.Authorization.ToString();
this.responseInfo.EndpointUri = httpClient.BaseAddress.AbsoluteUri;
// The async post.
this.responseInfo.ResponseMessage = await httpClient.PostAsJsonAsync(access.EndpointDirectory, stringContent).ConfigureAwait(false);
this.responseInfo.ResponseStatusCode = (int)this.responseInfo.ResponseMessage.StatusCode;
// Consistently returns true so long as my credentials are valid.
// When the auth credentials are invalid, this returns false.
if (this.responseInfo.ResponseMessage.IsSuccessStatusCode)
{
// I expect to see some data from the service.
this.responseInfo.ResponseContent = this.responseInfo.ResponseMessage.Content.ReadAsStringAsync();
}
}
}
Try/Catch blocks and some company specific info is omitted. The responseInfo object above is just a model with some properties to collect information from this method, so we can record the event.
Where I suspect a problem may be, is in the code below the PostAsJsonAsync command. But I'm at a loss for what to do there. Thanks for your help.

This (slightly adjusted) is what you want to do (substitute your own variables as needed):
using (HttpClient httpClient = new HttpClient())
{
// ...
HttpResponseMessage responseMessage = await httpClient.PostAsJsonAsync(access.EndpointDirectory, stringContent).ConfigureAwait(false);
// ...
string responseBody = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
// ...
}
I.e. you must await ReadAsStringAsync() to get the actual content.
For completeness, note that HttpResponseMessage and HttpResponseMessage.Content are IDisposable.

Related

Trying to pass several headers in get request [duplicate]

I'm trying to set the Content-Type header of an HttpClient object as required by an API I am calling.
I tried setting the Content-Type like below:
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("http://example.com/");
httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
// ...
}
It allows me to add the Accept header but when I try to add Content-Type it throws the following exception:
Misused header name. Make sure request headers are used with
HttpRequestMessage, response headers with HttpResponseMessage, and
content headers with HttpContent objects.
How can I set the Content-Type header in a HttpClient request?
The content type is a header of the content, not of the request, which is why this is failing. AddWithoutValidation as suggested by Robert Levy may work, but you can also set the content type when creating the request content itself (note that the code snippet adds application/json in two places-for Accept and Content-Type headers):
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://example.com/");
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "relativeAddress");
request.Content = new StringContent("{\"name\":\"John Doe\",\"age\":33}",
Encoding.UTF8,
"application/json");//CONTENT-TYPE header
client.SendAsync(request)
.ContinueWith(responseTask =>
{
Console.WriteLine("Response: {0}", responseTask.Result);
});
For those who didn't see Johns comment to carlos solution ...
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
If you don't mind a small library dependency, Flurl.Http [disclosure: I'm the author] makes this uber-simple. Its PostJsonAsync method takes care of both serializing the content and setting the content-type header, and ReceiveJson deserializes the response. If the accept header is required you'll need to set that yourself, but Flurl provides a pretty clean way to do that too:
using Flurl.Http;
var result = await "http://example.com/"
.WithHeader("Accept", "application/json")
.PostJsonAsync(new { ... })
.ReceiveJson<TResult>();
Flurl uses HttpClient and Json.NET under the hood, and it's a PCL so it'll work on a variety of platforms.
PM> Install-Package Flurl.Http
try to use TryAddWithoutValidation
var client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
.Net tries to force you to obey certain standards, namely that the Content-Type header can only be specified on requests that have content (e.g. POST, PUT, etc.). Therefore, as others have indicated, the preferred way to set the Content-Type header is through the HttpContent.Headers.ContentType property.
With that said, certain APIs (such as the LiquidFiles Api, as of 2016-12-19) requires setting the Content-Type header for a GET request. .Net will not allow setting this header on the request itself -- even using TryAddWithoutValidation. Furthermore, you cannot specify a Content for the request -- even if it is of zero-length. The only way I could seem to get around this was to resort to reflection. The code (in case some else needs it) is
var field = typeof(System.Net.Http.Headers.HttpRequestHeaders)
.GetField("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
?? typeof(System.Net.Http.Headers.HttpRequestHeaders)
.GetField("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
if (field != null)
{
var invalidFields = (HashSet<string>)field.GetValue(null);
invalidFields.Remove("Content-Type");
}
_client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "text/xml");
Edit:
As noted in the comments, this field has different names in different versions of the dll. In the source code on GitHub, the field is currently named s_invalidHeaders. The example has been modified to account for this per the suggestion of #David Thompson.
For those who troubled with charset
I had very special case that the service provider didn't accept charset, and they refuse to change the substructure to allow it...
Unfortunately HttpClient was setting the header automatically through StringContent, and no matter if you pass null or Encoding.UTF8, it will always set the charset...
Today i was on the edge to change the sub-system; moving from HttpClient to anything else, that something came to my mind..., why not use reflection to empty out the "charset"? ...
And before i even try it, i thought of a way, "maybe I can change it after initialization", and that worked.
Here's how you can set the exact "application/json" header without "; charset=utf-8".
var jsonRequest = JsonSerializeObject(req, options); // Custom function that parse object to string
var stringContent = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
stringContent.Headers.ContentType.CharSet = null;
return stringContent;
Note: The null value in following won't work, and append "; charset=utf-8"
return new StringContent(jsonRequest, null, "application/json");
EDIT
#DesertFoxAZ suggests that also the following code can be used and works fine. (didn't test it myself, if it work's rate and credit him in comments)
stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
Some extra information about .NET Core (after reading erdomke's post about setting a private field to supply the content-type on a request that doesn't have content)...
After debugging my code, I can't see the private field to set via reflection - so I thought I'd try to recreate the problem.
I have tried the following code using .Net 4.6:
HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, #"myUrl");
httpRequest.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
Task<HttpResponseMessage> response = client.SendAsync(httpRequest); //I know I should have used async/await here!
var result = response.Result;
And, as expected, I get an aggregate exception with the content "Cannot send a content-body with this verb-type."
However, if i do the same thing with .NET Core (1.1) - I don't get an exception. My request was quite happily answered by my server application, and the content-type was picked up.
I was pleasantly surprised about that, and I hope it helps someone!
Call AddWithoutValidation instead of Add (see this MSDN link).
Alternatively, I'm guessing the API you are using really only requires this for POST or PUT requests (not ordinary GET requests). In that case, when you call HttpClient.PostAsync and pass in an HttpContent, set this on the Headers property of that HttpContent object.
The trick is that you can just set all kinds of headers like:
HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("Accept-Language", "en"); //works OK
but not any header. For example:
request.Headers.Add("Content-Type", "application/json");//wrong
will raise the run-time exception Misused header name. It may seem that this will work:
request.Headers.Add(
HttpRequestHeader.ContentType.ToString(), //useless
"application/json"
);
but this gives a useless header named ContentType, without the hyphen. Header names are not case-sensitive, but are very hyphen-sensitive.
The solution is to declare the encoding and type of the body when adding the body to the Content part of the http request:
string Body = "...";
request.Content =
new StringContent(Body, Encoding.UTF8, "application/json");
Only then the applicable http header is automatically added to the request:
Content-Type: application/json; charset=utf-8
It was hard to find this out, with Fiddler, on a machine without a proxy server. Visual Studio used to have a Network Tool where you could inspect all headers, but only in version 2015, not in newer versions 2017 or 2022. If you use the debugger to inspect request.Headers, you will not find the header added automagically by StringContent().
var content = new JsonContent();
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("charset", "utf-8"));
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("IEEE754Compatible", "true"));
It's all what you need.
With using Newtonsoft.Json, if you need a content as json string.
public class JsonContent : HttpContent
{
private readonly MemoryStream _stream = new MemoryStream();
~JsonContent()
{
_stream.Dispose();
}
public JsonContent(object value)
{
Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var contexStream = new MemoryStream())
using (var jw = new JsonTextWriter(new StreamWriter(contexStream)) { Formatting = Formatting.Indented })
{
var serializer = new JsonSerializer();
serializer.Serialize(jw, value);
jw.Flush();
contexStream.Position = 0;
contexStream.WriteTo(_stream);
}
_stream.Position = 0;
}
private JsonContent(string content)
{
Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var contexStream = new MemoryStream())
using (var sw = new StreamWriter(contexStream))
{
sw.Write(content);
sw.Flush();
contexStream.Position = 0;
contexStream.WriteTo(_stream);
}
_stream.Position = 0;
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return _stream.CopyToAsync(stream);
}
protected override bool TryComputeLength(out long length)
{
length = _stream.Length;
return true;
}
public static HttpContent FromFile(string filepath)
{
var content = File.ReadAllText(filepath);
return new JsonContent(content);
}
public string ToJsonString()
{
return Encoding.ASCII.GetString(_stream.GetBuffer(), 0, _stream.GetBuffer().Length).Trim();
}
}
It appears that Microsoft tries to force the developers to follow their standards, without even giving any options or settings to do otherwise, which is really a shame especially given that this is a client and we are dictated by the server side requirements, especially given that Microsoft server side frameworks themselves require it!
So basically Microsoft tries to force us good habits when connecting to their server technologies that force us non good habits...
If anyone from Microsoft is reading this, then please fix it...
Either way for anyone that needs the content-type header for Get etc., while in an older .Net version it is possible to use the answer of #erdomke at https://stackoverflow.com/a/41231353/640195 this unfortunately no longer works in the newer .Net core versions.
The following code has been tested to work with .Net core 3.1 and from the source code on GitHub it looks like it should work with newer .Net versions as well.
private void FixContentTypeHeaders()
{
var assembly = typeof(System.Net.Http.Headers.HttpRequestHeaders).Assembly;
var assemblyTypes = assembly.GetTypes();
var knownHeaderType = assemblyTypes.FirstOrDefault(n => n.Name == "KnownHeader");
var headerTypeField = knownHeaderType?
.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.FirstOrDefault(n => n.Name.Contains("HeaderType"));
if (headerTypeField is null) return;
var headerTypeFieldType = headerTypeField.FieldType;
var newValue = Enum.Parse(headerTypeFieldType, "All");
var knownHeadersType = assemblyTypes.FirstOrDefault(n => n.Name == "KnownHeaders");
var contentTypeObj = knownHeadersType.GetFields().FirstOrDefault(n => n.Name == "ContentType").GetValue(null);
if (contentTypeObj is null) return;
headerTypeField.SetValue(contentTypeObj, newValue);
}
You can use this it will be work!
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Get,"URL");
msg.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
HttpResponseMessage response = await _httpClient.SendAsync(msg);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
Ok, it's not HTTPClient but if u can use it, WebClient is quite easy:
using (var client = new System.Net.WebClient())
{
client.Headers.Add("Accept", "application/json");
client.Headers.Add("Content-Type", "application/json; charset=utf-8");
client.DownloadString(...);
}
try to use HttpClientFactory
services.AddSingleton<WebRequestXXX>()
.AddHttpClient<WebRequestXXX>("ClientX", config =>
{
config.BaseAddress = new System.Uri("https://jsonplaceholder.typicode.com");
config.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
config.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
});
======================
public class WebRequestXXXX
{
private readonly IHttpClientFactory _httpClientFactory;
public WebRequestXXXX(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public List<Posts> GetAllPosts()
{
using (var _client = _httpClientFactory.CreateClient("ClientX"))
{
var response = _client.GetAsync("/posts").Result;
if (response.IsSuccessStatusCode)
{
var itemString = response.Content.ReadAsStringAsync().Result;
var itemJson = System.Text.Json.JsonSerializer.Deserialize<List<Posts>>(itemString,
new System.Text.Json.JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return itemJson;
}
else
{
return new List<Posts>();
}
}
}
}
I got the answer whith RestSharp:
private async Task<string> GetAccessTokenAsync()
{
var client = new RestClient(_baseURL);
var request = new RestRequest("auth/v1/login", Method.POST, DataFormat.Json);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("x-api-key", _apiKey);
request.AddHeader("Accept-Language", "br");
request.AddHeader("x-client-tenant", "1");
...
}
It worked for me.
You need to do it like this:
HttpContent httpContent = new StringContent(#"{ the json string }");
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage message = client.PostAsync(#"{url}", httpContent).Result;
For those wanting to set the Content-Type to Json specifically, you can use the extension method PostAsJsonAsync.
using System.Net.Http.Json; //this is needed for PostAsJsonAsync to work
//....
HttpClient client = new HttpClient();
HttpResponseMessage response = await
client.PostAsJsonAsync("http://example.com/" + "relativeAddress",
new
{
name = "John Doe",
age = 33
});
//Do what you need to do with your response
The advantage here is cleaner code and you get to avoid stringified json. More details can be found at: https://learn.microsoft.com/en-us/previous-versions/aspnet/hh944339(v=vs.118)
I find it most simple and easy to understand in the following way:
async Task SendPostRequest()
{
HttpClient client = new HttpClient();
var requestContent = new StringContent(<content>);
requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync(<url>, requestContent);
var responseString = await response.Content.ReadAsStringAsync();
}
...
SendPostRequest().Wait();
I end up having similar issue.
So I discovered that the Software PostMan can generate code when clicking the "Code" button at upper/left corner. From that we can see what going on "under the hood" and the HTTP call is generated in many code language; curl command, C# RestShart, java, nodeJs, ...
That helped me a lot and instead of using .Net base HttpClient I ended up using RestSharp nuget package.
Hope that can help someone else!
Api returned
"Unsupported Media Type","status":415
Adding ContentType to the jsonstring did the magic and this is my script working 100% as of today
using (var client = new HttpClient())
{
var endpoint = "api/endpoint;
var userName = "xxxxxxxxxx";
var passwd = "xxxxxxxxxx";
var content = new StringContent(jsonString, Encoding.UTF8, "application/json");
var authToken = Encoding.ASCII.GetBytes($"{userName}:{passwd}");
client.BaseAddress = new Uri("https://example.com/");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));
HttpResponseMessage response = await client.PostAsync(endpoint, content);
if (response.IsSuccessStatusCode)
{
// Get the URI of the created resource.
Uri returnUrl = response.Headers.Location;
Console.WriteLine(returnUrl);
}
string responseBody = await response.Content.ReadAsStringAsync();
return responseBody;
}
For my scenario, a third-party API was creating the HttpRequestMessage, so I was not able to use the top-voted answers to resolve the issue. And I didn't like the idea of messing with reflection so the other answers didn't work either.
Instead, I extended from AndroidMessageHandler and used this new class as a parameter to HttpClient. AndroidMessageHandler contains the method SendAsync which can be overridden in order to make changes to the HttpRequestMessage object before it is sent. If you don't have access to the Android Xamarin libaries, you may be able to figure something out with HttpMessageHandler.
public class XamarinHttpMessageHandler : global::Xamarin.Android.Net.AndroidMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Here I make check that I'm only modifying a specific request
// and not all of them.
if (request.RequestUri != null && request.RequestUri.AbsolutePath.EndsWith("download") && request.Content != null)
{
request.Content.Headers.Add("Content-Type", "text/plain");
}
return base.SendAsync(request, cancellationToken);
}
}
Then to use:
var client = new HttpClient(new XamarinHttpMessageHandler());
So if you're trying to do a /$batch OData request like this Microsoft article demonstrates where you're supposed to have a Content-Type header like:
Content-Type: multipart/mixed;boundary=batch_d3bcb804-ee77-4921-9a45-761f98d32029
string headerValue = "multipart/mixed;boundary=batch_d3bcb804-ee77-4921-9a45-761f98d32029";
//You need to set it like thus:
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(headerValue);
Again, the magic you need is: MediaTypeHeaderValue.Parse(...)
stringContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
And 🎉 YES! 🎉 ... that cleared up the problem with ATS REST API: SharedKey works now! 😄 👍 🍻
Source: https://github.com/dotnet/runtime/issues/17036#issuecomment-212046628

.net core 3.1 clientFactory pass json string in request body [duplicate]

I'm trying to POST a JsonObject using HttpClient from Web API. I'm not quite sure how to go about this and can't find much in the way of sample code.
Here's what I have so far:
var myObject = (dynamic)new JsonObject();
myObject.Data = "some data";
myObject.Data2 = "some more data";
HttpClient httpClient = new HttpClient("myurl");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = httpClient.Post("", ???);
I think I need to cast my JsonObject as a StreamContent but I'm getting hung up on that step.
With the new version of HttpClient and without the WebApi package it would be:
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
var result = client.PostAsync(url, content).Result;
Or if you want it async:
var result = await client.PostAsync(url, content);
The easiest way is to use a StringContent, with the JSON representation of your JSON object.
httpClient.Post(
"",
new StringContent(
myObject.ToString(),
Encoding.UTF8,
"application/json"));
Depending on your .NET version you could also use HttpClientExtensions.PostAsJsonAsync method.
https://msdn.microsoft.com/en-us/library/system.net.http.httpclientextensions.postasjsonasync.aspx
If using Newtonsoft.Json:
using Newtonsoft.Json;
using System.Net.Http;
using System.Text;
public static class Extensions
{
public static StringContent AsJson(this object o)
=> new StringContent(JsonConvert.SerializeObject(o), Encoding.UTF8, "application/json");
}
Example:
var httpClient = new HttpClient();
var url = "https://www.duolingo.com/2016-04-13/login?fields=";
var data = new { identifier = "username", password = "password" };
var result = await httpClient.PostAsync(url, data.AsJson())
I don't have enough reputation to add a comment on the answer from pomber so I'm posting another answer. Using pomber's approach I kept receiving a "400 Bad Request" response from an API I was POSTing my JSON request to (Visual Studio 2017, .NET 4.6.2). Eventually the problem was traced to the "Content-Type" header produced by StringContent() being incorrect (see https://github.com/dotnet/corefx/issues/7864).
tl;dr
Use pomber's answer with an extra line to correctly set the header on the request:
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var result = client.PostAsync(url, content).Result;
I spent hours trying to solve this.
But #anthls anwser saved my skin.
var data = new StringContent(JsonConvert.SerializeObject(new
{
abc = "jsjs",
xyz = "hhhh"
}));
data.Headers.ContentType = new MediaTypeHeaderValue("application/json"); // <--
var response = await client.PostAsync(url, data);
the code over it in vbnet:
dim FeToSend as new (object--> define class)
Dim client As New HttpClient
Dim content = New StringContent(FeToSend.ToString(), Encoding.UTF8,"application/json")
content.Headers.ContentType = New MediaTypeHeaderValue( "application/json" )
Dim risp = client.PostAsync(Chiamata, content).Result
msgbox(risp.tostring)
Hope this help
Thank you pomber but for
var result = client.PostAsync(url, content).Result;
I used
var result = await client.PostAsync(url, content);
because Result makes app lock for high request
I Faced same issue i.e
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
gave
"400 Bad Request"
Serializing JsonObject separately and passing the string in StringContent() solved issue for me, no need to set Encoding.UTF8 separately.
I want to answer all in one response when doing this job as a note for all and myself:
According to Serez's answer HttpContent derived classes list as below
https://stackoverflow.com/a/42380027/914284
HttpClient PostAsync has some background depending on the context you working on!
You can post data by the type that you want to send to server in cases
Server context waits it as bellow
[HttpPost]
public async Task<IActionResult> Submit(MyModel model)
[HttpPost]
public async Task<IActionResult> Submit([FromForm] MyModel model)
[HttpPost]
public async Task<IActionResult> Submit([FromBody] MyModel model)
When writing FromForm or Body it has working as FromForm.
FromBody needs json content otherwise it requires KeyValuePairs as rows. There is some implementations for both of them such as below:
For FromForm: I have used an extension
public static class HelperExtensions
{
public static FormUrlEncodedContent ToFormData(this object obj)
{
var formData = obj.ToKeyValue();
return new FormUrlEncodedContent(formData);
}
public static IDictionary<string, string> ToKeyValue(this object metaToken)
{
if (metaToken == null)
{
return null;
}
// Added by me: avoid cyclic references
var serializer = new JsonSerializer { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
if (metaToken is not JToken token)
{
// Modified by me: use serializer defined above
return ToKeyValue(JObject.FromObject(metaToken, serializer));
}
if (token.HasValues)
{
var contentData = new Dictionary<string, string>();
foreach (var child in token.Children().ToList())
{
var childContent = child.ToKeyValue();
if (childContent != null)
{
contentData = contentData.Concat(childContent)
.ToDictionary(k => k.Key, v => v.Value);
}
}
return contentData;
}
var jValue = token as JValue;
if (jValue?.Value == null)
{
return null;
}
var value = jValue?.Type == JTokenType.Date ?
jValue?.ToString("o", CultureInfo.InvariantCulture) :
jValue?.ToString(CultureInfo.InvariantCulture);
return new Dictionary<string, string> { { token.Path, value } };
}
}
For FromBody: Use any json converter library Newtonsoft or microsoft
using Newtonsoft.Json;
var jsonString = JsonConvert.SerializeObject(obj);
In both of them, content type should be defined according the requirement, for example for json (Write to header)
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
or another usage
using (var content = new StringContent(JsonConvert.SerializeObject(answer), System.Text.Encoding.UTF8, "application/json"))
{
var answerResponse = await client.PostAsync(url, content);
//use await it has moved in some context on .core 6.0
}
If you should use authorization on the context also you can provide authorization as below:
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");

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

.NET HttpClient Request Content-Type

I'm not sure, but it appears to me that the default implementation of .NET HttpClient library is flawed. It looks like it sets the Content-Type request value to "text/html" on a PostAsJsonAsync call. I've tried to reset the request value, but not sure if I'm doing this correctly. Any suggestions.
public async Task<string> SendPost(Model model)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsJsonAsync(Url + "api/foo/", model);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
You should set the content type. With the Accept you define what you want as response.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
The Accept request-header field can be used to specify certain media types which are acceptable for the response. Accept headers can be used to indicate that the request is specifically limited to a small set of desired types, as in the case of a request for an in-line image.
public async Task<string> SendPost(Model model)
{
var client = new HttpClient(); //You should extract this and reuse the same instance multiple times.
var request = new HttpRequestMessage(HttpMethod.Post, Url + "api/foo");
using(var content = new StringContent(Serialize(model), Encoding.UTF8, "application/json"))
{
request.Content = content;
var response = await client.SendAsync(request).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}

POSTing JsonObject With HttpClient From Web API

I'm trying to POST a JsonObject using HttpClient from Web API. I'm not quite sure how to go about this and can't find much in the way of sample code.
Here's what I have so far:
var myObject = (dynamic)new JsonObject();
myObject.Data = "some data";
myObject.Data2 = "some more data";
HttpClient httpClient = new HttpClient("myurl");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = httpClient.Post("", ???);
I think I need to cast my JsonObject as a StreamContent but I'm getting hung up on that step.
With the new version of HttpClient and without the WebApi package it would be:
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
var result = client.PostAsync(url, content).Result;
Or if you want it async:
var result = await client.PostAsync(url, content);
The easiest way is to use a StringContent, with the JSON representation of your JSON object.
httpClient.Post(
"",
new StringContent(
myObject.ToString(),
Encoding.UTF8,
"application/json"));
Depending on your .NET version you could also use HttpClientExtensions.PostAsJsonAsync method.
https://msdn.microsoft.com/en-us/library/system.net.http.httpclientextensions.postasjsonasync.aspx
If using Newtonsoft.Json:
using Newtonsoft.Json;
using System.Net.Http;
using System.Text;
public static class Extensions
{
public static StringContent AsJson(this object o)
=> new StringContent(JsonConvert.SerializeObject(o), Encoding.UTF8, "application/json");
}
Example:
var httpClient = new HttpClient();
var url = "https://www.duolingo.com/2016-04-13/login?fields=";
var data = new { identifier = "username", password = "password" };
var result = await httpClient.PostAsync(url, data.AsJson())
I don't have enough reputation to add a comment on the answer from pomber so I'm posting another answer. Using pomber's approach I kept receiving a "400 Bad Request" response from an API I was POSTing my JSON request to (Visual Studio 2017, .NET 4.6.2). Eventually the problem was traced to the "Content-Type" header produced by StringContent() being incorrect (see https://github.com/dotnet/corefx/issues/7864).
tl;dr
Use pomber's answer with an extra line to correctly set the header on the request:
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var result = client.PostAsync(url, content).Result;
I spent hours trying to solve this.
But #anthls anwser saved my skin.
var data = new StringContent(JsonConvert.SerializeObject(new
{
abc = "jsjs",
xyz = "hhhh"
}));
data.Headers.ContentType = new MediaTypeHeaderValue("application/json"); // <--
var response = await client.PostAsync(url, data);
the code over it in vbnet:
dim FeToSend as new (object--> define class)
Dim client As New HttpClient
Dim content = New StringContent(FeToSend.ToString(), Encoding.UTF8,"application/json")
content.Headers.ContentType = New MediaTypeHeaderValue( "application/json" )
Dim risp = client.PostAsync(Chiamata, content).Result
msgbox(risp.tostring)
Hope this help
Thank you pomber but for
var result = client.PostAsync(url, content).Result;
I used
var result = await client.PostAsync(url, content);
because Result makes app lock for high request
I Faced same issue i.e
var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
gave
"400 Bad Request"
Serializing JsonObject separately and passing the string in StringContent() solved issue for me, no need to set Encoding.UTF8 separately.
I want to answer all in one response when doing this job as a note for all and myself:
According to Serez's answer HttpContent derived classes list as below
https://stackoverflow.com/a/42380027/914284
HttpClient PostAsync has some background depending on the context you working on!
You can post data by the type that you want to send to server in cases
Server context waits it as bellow
[HttpPost]
public async Task<IActionResult> Submit(MyModel model)
[HttpPost]
public async Task<IActionResult> Submit([FromForm] MyModel model)
[HttpPost]
public async Task<IActionResult> Submit([FromBody] MyModel model)
When writing FromForm or Body it has working as FromForm.
FromBody needs json content otherwise it requires KeyValuePairs as rows. There is some implementations for both of them such as below:
For FromForm: I have used an extension
public static class HelperExtensions
{
public static FormUrlEncodedContent ToFormData(this object obj)
{
var formData = obj.ToKeyValue();
return new FormUrlEncodedContent(formData);
}
public static IDictionary<string, string> ToKeyValue(this object metaToken)
{
if (metaToken == null)
{
return null;
}
// Added by me: avoid cyclic references
var serializer = new JsonSerializer { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
if (metaToken is not JToken token)
{
// Modified by me: use serializer defined above
return ToKeyValue(JObject.FromObject(metaToken, serializer));
}
if (token.HasValues)
{
var contentData = new Dictionary<string, string>();
foreach (var child in token.Children().ToList())
{
var childContent = child.ToKeyValue();
if (childContent != null)
{
contentData = contentData.Concat(childContent)
.ToDictionary(k => k.Key, v => v.Value);
}
}
return contentData;
}
var jValue = token as JValue;
if (jValue?.Value == null)
{
return null;
}
var value = jValue?.Type == JTokenType.Date ?
jValue?.ToString("o", CultureInfo.InvariantCulture) :
jValue?.ToString(CultureInfo.InvariantCulture);
return new Dictionary<string, string> { { token.Path, value } };
}
}
For FromBody: Use any json converter library Newtonsoft or microsoft
using Newtonsoft.Json;
var jsonString = JsonConvert.SerializeObject(obj);
In both of them, content type should be defined according the requirement, for example for json (Write to header)
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
or another usage
using (var content = new StringContent(JsonConvert.SerializeObject(answer), System.Text.Encoding.UTF8, "application/json"))
{
var answerResponse = await client.PostAsync(url, content);
//use await it has moved in some context on .core 6.0
}
If you should use authorization on the context also you can provide authorization as below:
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");

Categories