problems with postasync HttpContent - c#

im using PostAsync to call my external webAPI programatically in my back end.
I have had the method working using a httpget, however turning it into a post seems more problematic, due to the httpcontent object required. when awaiting, the status code is always a 404 due to the data not being passed over.
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:52715/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var data = JsonConvert.SerializeObject(new { user = "JOHNSMITH" });
HttpResponseMessage response = await client.PostAsync("MessageUser", new StringContent(data, Encoding.UTF8, "text/json"));
if (response.IsSuccessStatusCode)
{
Console.WriteLine("\n sent signal to dept");
}
}

Related

C# API Beginner

I have the following code as a start to create a API Call to https://jsonplaceholder.typicode.com/posts/. I want to practice making the call, receive a JSON response and then..do stuff.
How can I finish this off to get a response so I can iterate through the response array.
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(URL);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get,"");
request.Content = new StringContent(URL, Encoding.UTF8,"application/json");
Wiring this in VS Code so will need to install packages if needed.
Thank you!
You are almost there. Try (if you want a simple synchronous send):
HttpClient client = new HttpClient();
string responseString;
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri("<insert your URL>"))) {
HttpResponseMessage response = client.SendAsync(request).Result;
// Get the response content as a string
responseString = response.Content.ReadAsStringAsync().Result;
}
Note that it's good practice to initialize one instance of HttpClient and reuse it to send multiple requests (rather than initialize one every time you need to send something).
Any headers, URLs and such specific to a message should be set in the HttpRequestMessage class (which should be disposed of with the "using ..." term.

Getting Invalid Content Type doing a Delete httpclient call. What am I doing wrong?

When I try to do the code below, it just results in Invalid Content Type (with error number 612).
I'm trying to delete a lead id from a static list. I can add lead ids or get the static list leads fine.
The post and get calls I make are working fine, although the post calls I make seem to require the data right on the url string (as in $"{endpointURL}/rest/v1/lists/{listID}/leads.json?id={leadID}"; If I include the id as a json object, it fails too. This might be a clue to what I'm doing wrong with the delete call.
string url = $"{endpointURL}/rest/v1/lists/{listID}/leads.json?id={leadID}";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Authorization = new
AuthenticationHeaderValue("Bearer", _access_token);
HttpResponseMessage response = await client.DeleteAsync(url);
The response here always results in Invalid Content Type.
If I add this line before I do the deleteasync call, it gives me a different error before it even hits the deleteAsync call.
client.DefaultRequestHeaders.Add("Content-Type", "application/json");
Error is "Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects."
Try using HttpRequestMessage in your code like this
string url = $"{endpointURL}/rest/";
HttpClient client = new HttpClient
{
BaseAddress = new Uri(url)
};
//I'm assuming you have leadID as an int parameter in the method signature
Dictionary<string, int> jsonValues = new Dictionary<string, int>();
jsonValues.Add("id", leadID);
//create an instance of an HttpRequestMessage() and pass in the api end route and HttpMethod
//along with the headers
HttpRequestMessage request = new HttpRequestMessage
(HttpMethod.Delete, $"v1/lists/{listID}") //<--I had to remove the leads.json part of the route... instead I'm going to take a leap of faith and hit this end point with the HttpMethod Delete and pass in a Id key value pair and encode it as application/json
{
Content = new StringContent(new JavaScriptSerializer().Serialize(jsonValues), Encoding.UTF8, "application/json")
};
request.Headers.Add("Bearer", _access_token);
//since we've already told the request what type of httpmethod we're using
//(in this case: HttpDelete)
//we could just use SendAsync and pass in the request as the argument
HttpResponseMessage response = await client.SendAsync(request);
The solution turned out to be a combination of a couple of suggestions.
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(url);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Delete, data);
// The key part was the line below
request.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
if (!string.IsNullOrEmpty(_access_token))
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _access_token);
}
HttpResponseMessage response = await client.SendAsync(request);
This worked for me.

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

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.

Custom header to HttpClient request

How do I add a custom header to a HttpClient request? I am using PostAsJsonAsync method to post the JSON. The custom header that I would need to be added is
"X-Version: 1"
This is what I have done so far:
using (var client = new HttpClient()) {
client.BaseAddress = new Uri("https://api.clickatell.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "xxxxxxxxxxxxxxxxxxxx");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.PostAsJsonAsync("rest/message", svm).Result;
}
I have found the answer to my question.
client.DefaultRequestHeaders.Add("X-Version","1");
That should add a custom header to your request
Here is an answer based on that by Anubis (which is a better approach as it doesn't modify the headers for every request) but which is more equivalent to the code in the original question:
using Newtonsoft.Json;
...
var client = new HttpClient();
var httpRequestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://api.clickatell.com/rest/message"),
Headers = {
{ HttpRequestHeader.Authorization.ToString(), "Bearer xxxxxxxxxxxxxxxxxxx" },
{ HttpRequestHeader.Accept.ToString(), "application/json" },
{ "X-Version", "1" }
},
Content = new StringContent(JsonConvert.SerializeObject(svm))
};
var response = client.SendAsync(httpRequestMessage).Result;
var request = new HttpRequestMessage {
RequestUri = new Uri("[your request url string]"),
Method = HttpMethod.Post,
Headers = {
{ "X-Version", "1" } // HERE IS HOW TO ADD HEADERS,
{ HttpRequestHeader.Authorization.ToString(), "[your authorization token]" },
{ HttpRequestHeader.ContentType.ToString(), "multipart/mixed" },//use this content type if you want to send more than one content type
},
Content = new MultipartContent { // Just example of request sending multipart request
new ObjectContent<[YOUR JSON OBJECT TYPE]>(
new [YOUR JSON OBJECT TYPE INSTANCE](...){...},
new JsonMediaTypeFormatter(),
"application/json"), // this will add 'Content-Type' header for the first part of request
new ByteArrayContent([BINARY DATA]) {
Headers = { // this will add headers for the second part of request
{ "Content-Type", "application/Executable" },
{ "Content-Disposition", "form-data; filename=\"test.pdf\"" },
},
},
},
};
There is a Headers property in the HttpRequestMessage class. You can add custom headers there, which will be sent with each HTTP request. The DefaultRequestHeaders in the HttpClient class, on the other hand, sets headers to be sent with each request sent using that client object, hence the name Default Request Headers.
Hope this makes things more clear, at least for someone seeing this answer in future.
I have added x-api-version in HttpClient headers as below :
var client = new HttpClient(httpClientHandler)
{
BaseAddress = new Uri(callingUrl)
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("x-api-version", v2);
My two cents. I agree with heug. The accepted answer is a mind bender. Let's take a step back.
Default headers apply to all requests made by a particular HttpClient. Hence you would use default headers for shared headers.
_client.DefaultRequestHeaders.UserAgent.ParseAdd(_options.UserAgent);
However, we sometimes need headers specific to a certain request. We would therefore use something like this in the method:
public static async Task<HttpResponseMessage> GetWithHeadersAsync(this
HttpClient httpClient, string requestUri, Dictionary<string, string> headers)
{
using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri))
{
foreach(var header in headers)
{
request.Headers.Add(header.Key, header.Value);
}
return await httpClient.SendAsync(request);
}
}
If you only need one additional non-default header you would simply use:
request.Headers.Add("X-Version","1")
For more help:
How to add request headers when using HttpClient
Also you can use HttpClient.PostAsync method:
using (HttpClient http_client = new HttpClient())
{
StringContent string_content = new StringContent(JsonSerializer.Serialize("{\"field\":\"field_value\"}"));
string_content.Headers.Add("X-Version","1");
using (HttpResponseMessage response = await http_client.PostAsync("http://127.0.0.1/apiMethodName", string_content))
{
if (response.IsSuccessStatusCode)
{
Console.WriteLine($"Response status code: {response.StatusCode}");
}
else
{
Console.WriteLine($"Error status code: {response.StatusCode}");
}
}
}
Just in case someone is wondering how to call httpClient.GetStreamAsync() which does not have an overload which takes HttpRequestMessage to provide custom headers you can use the above code given by #Anubis and call
await response.Content.ReadAsStreamAsync()
Especially useful if you are returning a blob url with Range Header as a FileStreamResult

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

Categories