When testing the call through Postman and a C# WebRequest it works, but I am unable to do the same using an HttpClient with a PostAsync or PostJsonAsync call.
Error: Unsupported Media Type, although application/json is required and applied.
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new StringContent(data, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync("https://pos.api.here.com/positioning/v1/locate?app_id={id}&app_code={code}", content);
return response;
StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1,
Content: System.Net.Http.HttpConnection+HttpConnectionResponseContent,
Headers:{ Date: Fri, 08 Nov 2019 13:38:37 GMT Server: nginx-clojure
Content-Type: application/json Content-Length: 114}
WebRequest
HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
if (!string.IsNullOrEmpty(data))
{
request.ContentType = "application/json";
request.Method = "POST";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(data);
streamWriter.Flush();
streamWriter.Close();
}
}
using (HttpWebResponse webresponse = request.GetResponse() as HttpWebResponse)
{
using (StreamReader reader = new StreamReader(webresponse.GetResponseStream()))
{
string response = reader.ReadToEnd();
return response;
}
}
There are two differences I see:
You are setting the Accept header in your HttpClient code, where you were not in your WebRequest code. This defines the type of data that you accept. If this API call does not return JSON, then it could be just saying "I have nothing to say to you then". You can try just deleting that whole line.
The Content-Type in your HttpClient code will be application/json; charset=utf-8, whereas you set it to just application/json in your WebRequest code. I don't see why the charset would make it choke, but if changing #1 doesn't work, you can try setting the Content-Type directly and see if it makes any difference:
var content = new StringContent("");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
Related
I'm working with an external api and want to upload file using multipart/form-data. The working solution in curl is: curl --form file=#./fileName.mp4 "http://page.com"
According to https://curl.olsh.me/. The code I'm looking for is:
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "http://page.com/"))
{
var multipartContent = new MultipartFormDataContent();
multipartContent.Add(new ByteArrayContent(File.ReadAllBytes("./fileName.mp4")), "file", Path.GetFileName("./fileName.mp4"));
request.Content = multipartContent;
var response = await httpClient.SendAsync(request);
}
}
However it doesn't work. The headers and the file is different so I'm getting an error.
The code I've written and 'works', but the attachment is not valid, because encoding bytes to string is incorrect.
var webClient = new HttpClient();
webClient.DefaultRequestHeaders.ExpectContinue = true;
string boundary = "------------------------" + DateTime.Now.Ticks.ToString("x");
var fileData = Encoding.ASCII.GetString(System.IO.File.ReadAllBytes(pathToFile));
var package = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n{3}\r\n--{0}--\r\n", boundary, "fileName", "application/octet-stream", fileData);
var content = new StringContent(package);
content.Headers.Remove("Content-Type");
content.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
content.Headers.Remove("Content-Length");
content.Headers.Add("Content-Length", package.Length.ToString());
await webClient.PostAsync(address, content);
Edit:
The headers from curl request:
POST http://page.com/ HTTP/1.1
Host: page.com
User-Agent: curl/7.55.1
Accept: */*
Connection: Keep-Alive
Content-Length: 1703578
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------1a538744c5619c1e
--------------------------1a538744c5619c1e
Content-Disposition: form-data; name="file"; filename="cst.mp4"
Content-Type: application/octet-stream
The headers from the 1st example code:
POST http://page.com/ HTTP/1.1
Content-Type: multipart/form-data; boundary="735b20d4-4de4-46fb-8293-2c2e996ce180"
Content-Length: 1703552
Host: page.com
--735b20d4-4de4-46fb-8293-2c2e996ce180
Content-Disposition: form-data; name=file; filename=cst.mp4; filename*=utf-8''cst.mp4
I think the file size difference is a red herring here.
A common practice for web servers is to deny requests that don't specify a user agent. Try adding a user agent (or even spoofing the curl agent) as below:
webClient.DefaultRequestHeaders.Add("User-Agent", "curl/7.55.1");
The problem with the first code is that the boundary, file headers and output headers differs. I had to add two Content-Type headers - one to my file and one to the output. I was unable to add the correct headers and remove the wrong one, beacuse I tried to add headers to multipart instead of multipart content.
If you want to add headers to your multipart content you need to do it on HttpContent.
The working solution is:
string boundary = "------------------------" + DateTime.Now.Ticks.ToString("x");
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), address))
{
httpClient.DefaultRequestHeaders.Add("User-Agent", userAgentName);
httpClient.DefaultRequestHeaders.Add("Connection", "Keep-Alive");
httpClient.DefaultRequestHeaders.ExpectContinue = true;
var multipartContent = new MultipartFormDataContent(boundary);
multipartContent.Headers.Remove("Content-Type");
multipartContent.Headers.Add("Content-Type", "multipart/form-data; boundary="+boundary);
var bcd = new ByteArrayContent(System.IO.File.ReadAllBytes(path));
bcd.Headers.Clear();
bcd.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\""+fileName+"\"");
bcd.Headers.Add("Content-Type", "application/octet-stream");
multipartContent.Add(bcd, "file", fileName);
request.Content = multipartContent;
var response = await httpClient.SendAsync(request);
}
}
Now the headers from curl and the HttpClient are almost the same(Accept header is missing, but I don't need it).
I am trying to make an service that posts some data to an API Endpoint using C# HttpClient. The code is as follows.
public class HttpClientService : IHttpClientService
{
static HttpClient client = new HttpClient();
public HttpClientService()
{
client.BaseAddress = new Uri("http://xx.xx.xx.xx/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public async Task<Uri> MakeLogEntry(CnsLog log)
{
HttpResponseMessage response = await client.PostAsJsonAsync("api/logs", log);
return response.Headers.Location;
}
}
The problem is that the end point returns error 411 Length Required. I have found that this is because my request doesn't have the content-length header set, which I found to be true when I inspected the request using Fiddler.
I have tried to set the content length header on the client in the constructor but the code doesn't compile after that. I'm stuck and would appreciate any help. Thanks
You don't want to set the Content-Length header on the client, especially since it's a static instance. You want to set it on the individual request.
PostAsJsonAsync is a nice shortcut that builds the HttpContent from a poco, builds the HttpRequestMessage from that content, and sends the POST request. Handy, but all that abstraction doesn't give you the opportunity to set request-level headers. So, you need to do a little more work to build/send the request:
var json = JsonConvert.SerializeObject(log);
var content = new StringContent(json, Encoding.UTF8, "application/json");
content.Headers.ContentLength = json.Length;
var response = await client.PostAsync("api/logs", content);
Alternately you can use HttpWebRequest.
byte[] postBytes = Encoding.ASCII.GetBytes(log);
request.ContentLength = postBytes.Length;
Example of HttpWebRequest is as below:
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] data = encoder.GetBytes(serializedObject); // a json object, or xml, whatever...
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = data.Length;
request.Expect = "application/json";
request.GetRequestStream().Write(data, 0, data.Length);
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
I have a POST method that can post data perfectly.
Looking at docs it seems a PATCH (or PUT) should look the exact same, just use PutAsync instead of PostAsync.
Well doing just that I get the following error:
+ postResponse {StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.NoWriteNoSeekStreamContent, Headers:
{
Cache-Control: private
Date: Mon, 09 Oct 2017 12:19:28 GMT
Transfer-Encoding: chunked
request-id: 60370069-f7c4-421d-842e-b1ee8573c2c2
client-request-id: 60370069-f7c4-421d-842e-b1ee8573c2c2
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Europe","Slice":"SliceB","ScaleUnit":"002","Host":"AGSFE_IN_7","ADSiteName":"DUB"}}
Duration: 3.2626
Content-Type: application/json
}} System.Net.Http.HttpResponseMessage
And response:
Entity only allows writes with a JSON Content-Type header
And sure enough in the Error I can also see this:
ContentType {text/plain; charset=utf-8} System.Net.Http.Headers.MediaTypeHeaderValue
So the error makes sense, however, I do tell it to use JSON, and it work as intended in my POST-method with the same code:
public async Task UpdateToGraph(object UnSerializedContent, string relativeUrl)
{
string accessToken = await _tokenManager.GetAccessTokenAsync();
HttpContent content = new StringContent(JsonConvert.SerializeObject(UnSerializedContent));
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
Client.DefaultRequestHeaders.Add("ContentType", "application/json");
string endpoint = "https://graph.microsoft.com/v1.0" + relativeUrl;
var postResponse = Client.PutAsync(endpoint, content).Result;
string serverResponse = postResponse.Content.ReadAsStringAsync().Result;
}
You can use the .{Verb}AsJsonAsync HttpClientExtensions methods.
public async Task UpdateToGraph(object UnSerializedContent, string relativeUrl) {
var accessToken = await _tokenManager.GetAccessTokenAsync();
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
Client.DefaultRequestHeaders.Add("ContentType", "application/json");
var endpoint = "https://graph.microsoft.com/v1.0" + relativeUrl;
var postResponse = await Client.PutAsJsonAsync(endpoint, UnSerializedContent);
var serverResponse = await postResponse.Content.ReadAsStringAsync();
}
Also note the proper use of async/await by not mixing blocking calls like .Result with async methods as this can lead to deadlocks.
Reference Async/Await - Best Practices in Asynchronous Programming
Set the content type using the StringContent constructor:
HttpContent content = new StringContent(JsonConvert.SerializeObject(UnSerializedContent), System.Text.Encoding.UTF8, "application/json");
As far as I'm aware, you're not meant to set the content header on the request object when using the HttpClient.
I am trying to update a Confluence page.
I have been able to use this on Confluence localhost, but when I tried it on the production server I got this error:
StatusCode: 405, ReasonPhrase: 'Method Not Allowed', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Vary: Accept-Encoding
Date: Tue, 31 Jan 2017 21:29:44 GMT
Server: Apache/2.2.15
Server: (CentOS)
Content-Length: 342
Allow: GET
Allow: HEAD
Allow: POST
Allow: OPTIONS
Allow: TRACE
Content-Type: text/html; charset=iso-8859-1
}
This is my code. Any idea what would be causing this issue?
string json = "{\"version\":{\"number\":4},\"title\":\"Bloomberg Test\",\"type\":\"page\",\"body\":{\"storage\":{\"value\":\"Hello World\",\"representation\": \"storage\"}}}";
string userpass = username+":"+password;
string encoded = Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(userpass));
string encval = "Basic " + encoded;
var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Atlassian-Token", "nocheck");
client.DefaultRequestHeaders.Add("Authorization", encval);
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.BaseAddress = new Uri(baseurl);
var resp = client.PutAsync(#"/rest/api/content/"+pageid, content);
405 means that the HTTP method (GET, POST, PUT ...) is not allowed.
I don't know the details of Confluence, but try using a POST request
var resp = client.PostAsync(#"/rest/api/content/"+pageid, content);
You're performing an HTTP Put via client.PutAsync(). This is probably allowed locally, but on the server it's not. The response even includes the allowed http methods:
Allow: GET
Allow: HEAD
Allow: POST
Allow: OPTIONS
Allow: TRACE
So if the suggested PostAsync() is not supported by Confluence, adjust the server and allow the PUT method as well.
Replaced:
client.BaseAddress = new Uri(baseurl);
var resp = client.PutAsync(#"/rest/api/content/"+pageid, content);
With:
var resp = client.PutAsync(baseurl+"/rest/api/content/"+pageid, content);
My guess, the BaseAddress is doing something odd like adding a slash at the end or something.
It works now!
I want to use public Web Api from: https://www.xbtce.cc/tradeapi.
So here is a code:
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var url = "https://cryptottdemowebapi.xbtce.net:8443/api/v1/public/tick";
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
And result is:
{StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 15 Aug 2016 16:03:45 GMT
Content-Length: 0
}}
This error continue even after adding request headers with "application/json".
Any ideas how to fix this?
Seems like a broken server implementation, given if I load that URL in my browser, it most definitely returns application/json as the Content-Type for the response. It's not strictly correct, but might work for this (seemingly) broken server to pass an Accept header with wildcards, e.g. */* and see if that helps.
You forgot about the gzip
below the correct code
var client = new HttpClient(new PublicContentHandler());
client.BaseAddress = new Uri(_webApiAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
public class PublicContentHandler : HttpClientHandler
{
public PublicContentHandler()
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
}
}
Refer the link with the bunch of c# examples.