https request on winRT - c#

I need to send this request :
POST /params/initProvisioning HTTP/1.1
Host: serverIp:4443
Content-Type: application/json
Cache-Control: no-cache
{
"mobileOS":"WindowsPhone",
"pushId":"https://db3.notify.windows.com/?token=AwYAAAA...",
"deviceType":"type"
}
When I use PostMan tool to send it, everything works fine. But when I send it from my windows phone 8.1 app I receive an error 404 not found :
StringBuilder sb = new StringBuilder();
sb.Append("{");
foreach (Parameter p in parameters)
sb.Append(ParameterToJsonString(p));
sb.Append("}");
Debug.WriteLine("params to send : {0}", sb.ToString());
HttpClient httpClient = new HttpClient();
var response = await httpClient.PostAsync(new Uri("https://serverIp:4443/params/initProvisioning"),
new StringContent(sb.ToString(), Encoding.UTF8, "application/json"));
response.EnsureSuccessStatusCode();
Is there a special client for https protocol ?

Related

Difference in multipart/form-data usage in versions 106 and 107

I tried to update RestSharp version from 104 to 108.
Other APIs (application/json) fine.
But in multipart/form-data to transfer file, On server side, there is no file.
I tried to make them as similar as possible. But it still didn't work.
So I thought that there will be some changes as the version changes.
I found a version that doesn't work.
Working : Version 104.4.0, 106.15.0
Not working : Version 107.3.0, 108.0.3
Is there anything else I need to do to transfer files from version 107?
Version 106.15.0
var client = new RestClient("https://192.168.0.1/");
client.Timeout = -1;
var request = new RestRequest("file", Method.POST);
request.AlwaysMultipartFormData = true;
request.AddHeader("Authorization", "Auth-Token" + token);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Accept", "application/json");
request.AddFile("file", path);
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
var response = client.Execute(request);
Console.WriteLine(response.Content);
Capture from postman console
POST /file HTTP/1.1
Authorization: Auth-Token ----
Postman-Token: ecff7025-298c-4b29-8b15-ebd0d679aad8
Host: 192.168.0.1
Content-Type: multipart/form-data; boundary=--------------------------077067924352348455764323
Content-Length: 2409
----------------------------077067924352348455764323
Content-Disposition: form-data; name="file"; filename="filename.txt"
<filename.txt>
----------------------------077067924352348455764323--
Version 107.3.0
var options = new RestClientOptions("https://192.168.0.1/")
{
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
ConfigureMessageHandler = handler =>
new HttpTracerHandler(handler, new ConsoleLogger(), HttpMessageParts.All),
};
var client = new RestClient(options);
var request = new RestRequest("file", Method.Post);
request.AlwaysMultipartFormData = true;
request.AddHeader("Authorization", "Auth-Token" + token);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Accept", "application/json");
request.AddFile("file", path);
var response = client.ExecuteAsync(request).Result;
Console.WriteLine(response.Content);
Capture from HttpTracer
==================== HTTP REQUEST: [POST] ====================
POST https://192.168.0.1/file
Authorization: Auth-Token ----
Accept: application/json
User-Agent: RestSharp/0.0.0.0
--94000a34-a3fa-4eeb-b803-52eea1c7cbc9
Content-Type: application/octet-stream
Content-Disposition: form-data; name="file"; filename="filename.txt"
/////////////////////////////////
File contents ...
/////////////////////////////////
--94000a34-a3fa-4eeb-b803-52eea1c7cbc9--
Add :
nginx error log
upstream timed out (110: Connection timed out) while reading response header from upstream
Edit :
Added captured data of Post messages using postman and httptracer.
Edit2 :
Added nginx error log

How to fix Unable to send GET request - 403 error

I am new to the C# world, and can't for the life of me figure out how to get around this error. I am trying to make a simple get request using a platform API key. I have built out the API connection in Google App Script on the same laptop, and it works fine, but when trying to build out the same API in C#, it is returning:
{StatusCode: 403, ReasonPhrase: 'Forbidden', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Date: Sun, 13 Mar 2022 02:41:29 GMT
Transfer-Encoding: chunked
Connection: close
CF-Chl-Bypass: 1
Permissions-Policy: accelerometer=(),autoplay=(),camera=(),clipboard-read=(),clipboard-write=(),fullscreen=(),geolocation=(),gyroscope=(),hid=(),interest-cohort=(),magnetometer=(),microphone=(),payment=(),publickey-credentials-get=(),screen-wake-lock=(),serial=(),sync-xhr=(),usb=()
Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
X-Frame-Options: SAMEORIGIN
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Strict-Transport-Security: max-age=2592000
Server: cloudflare
CF-RAY: 6eb1692f8bd776c3-LHR
Content-Type: text/html; charset=utf-8
Expires: Thu, 01 Jan 1970 00:00:01 GMT
}}
The API documentation says:
"To authenticate against the API, include your API key in the 'Authorization' header, prefixed with 'Key ', in every request. Example: 'Authorization: Key yourapikey'"
And so, I have tried adding this to
a) the HttpClient via HttpClient.DefaultRequestHeaders.Authorization
b) the HttpClient via HttpClient.DefaultHeaders.Add
c) the HttpRequestMessage via HttpRequestMessage.Headers.Add
In each instance, the request URI looks good, as well as the headers, but still returning 403.
My current structure is:
// services
builder.Services.AddHttpClient("myplatform", c =>
{
c.BaseAddress = new Uri("https://seller-api.myplatform.com/v2/");
c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
});
// controller
// constructor uses IHttpClientFactory
this._httpClient = clientFactory.CreateClient("myplatform");
// service
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Key", platformKey);
string endPoint = "offers" + CreateQueryString(pageNumber, pageSize);
// example endPoint: offers?page_number=1&page_size=100
var requestMsg = new HttpRequestMessage()
{
Method = HttpMethod.Get,
RequestUri = new Uri(httpClient.BaseAddress + endPoint)
};
try
{
var result = await httpClient.SendAsync(requestMsg);
}
catch (Exception ex)
{
}
Again, the content of the call works when calling through GoogleAppScript. What am I doing wrong in C#, and how can I correct this?
Not sure if this is all the information needed - let me know otherwise! Important to note, the target framework is .NET 6.0.
EDIT
As suggested by a comment around duplicate clients, I have removed the client factory. I am now creating a new HttpClient in the controller constructor, and passing this client to my service to do the GET request.
this._httpClient = new HttpClient();
Again, the client and the request message look well formed at time of request but still returning 403 error. Is there an issue with my VS22 client, or web client etc.?
Also, the call I am making successfully via Google AppScript is using UrlFetchApp. Not sure what is the issue here with the C# side..
EDIT2
Adding current GAS code for reference:
var url = 'https://seller-api.platform.com/v2';
var end_point = '/offers?';
var header = {
'Authorization': api_key
}
var params = {
'method': 'GET',
'headers': header
}
// call API
var page_query = 'page_number=' + page + '&page_size=' + maxItemsPerPage;
var full_url = url + end_point + page_query;
var response = UrlFetchApp.fetch(full_url, params);
The HTTP 403 Forbidden response status code indicates that the server understands the request but refuses to authorize it.
There are two ways add request headers when using HttpClient:
Add headers for all requests using HttpClient.DefaultRequestHeaders.
HttpClient = new HttpClient();
HttpClient.DefaultRequestHeaders.Add("Key", platformKey);
var response = await HttpClient.GetAsync(GetRandomNumberUrl);
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync();
Add headers per request using HttpRequestMessage.Headers.
HttpClient = new HttpClient();
using (var request = new HttpRequestMessage(HttpMethod.Get, randomNumberUrl))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", platformKey);
var response = await HttpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync();
}
Your problem is here in this line:
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Key", platformKey);
your _httpClient and Authorization httpClient is not same instance.
Try this way:
HttpClient HttpClient = new HttpClient();
string url = "https://seller-api.platform.com/v2";
string end_point = "/offers?";
string api_key = "key here";
string page_query = "page_number=" + 10 + "&page_size=" + 20;
string full_url = url + end_point + page_query;
using (var request = new HttpRequestMessage(HttpMethod.Get, url))
{
request.Headers.Add("Authorization", api_key);
var response = await HttpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var m = await response.Content.ReadAsStringAsync();
}
Solution 2:
Try calling it like a browser :
HttpClient httpClient = new HttpClient();
string url = "https://gatewayt.whatever.com/chkt/request/request.php";
string end_point = "/offers?";
string api_key = "key here";
string page_query = "page_number=" + 10 + "&page_size=" + 20;
string full_url = url + end_point + page_query;
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/html,application/xhtml+xml,application/xml");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Charset", "ISO-8859-1");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Key", api_key);
var response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
await response.Content.ReadAsStringAsync();
source: Request like browser Link1 Link2 Link3

cURL call in c# bad request

I'm trying to do the following cURL call in a c# .net environment
curl -XPOST -d 'Metadata/Type = "sas"' http://bms.org/bcknd/republish
The C# code is as follows:
var requestContent = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("Metadata/Type", "\"sas\""), });
HttpResponseMessage response = await client.PostAsync("http://bms.org/bcknd/republish", requestContent);
HttpContent responseContent = response.Content;
using (var reader = new StreamReader(await responseContent.ReadAsStreamAsync()))
{
Console.WriteLine(await reader.ReadToEndAsync());
}
I'm getting a 400 Bad request and when I print it out. Maybe it has something to do with the -XPOST and -d parameter from the curl call?
EDIT:
Here's the http request from curl:
POST http://bms.org/bcknd/republish HTTP/1.1
Host: bms.org/bcknd
User-Agent: curl/7.48.0
Accept: */*
Content-Length: 43
Content-Type: application/x-www-form-urlencoded
Metadata/Type = "sas"
Here's the http request from my code:
POST http://bms.org/bcknd/republish HTTP/1.1
Accept: */*
User-Agent: curl/7.48.0
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Host: bms.org/bcknd
Content-Length: 43
Connection: Keep-Alive
Metadata/Type = "sas"
Short Version
Post the data as StringContent without url encoding and check the response status before trying to read the response body. Make sure the call completes before the application exits, otherwise the call will be cancelled when the application exits. That means, use async Task in Main, not async void :
class Program
{
static async Task Main(string[] args)
{
var client=new HttpClient();
var data = new StringContent("Metadata/Type=\"sas\"",Encoding.UTF8,"application/x-www-form-urlencoded");
var response = await client.PostAsync("http://www.google.com/bcknd/republish", data);
if(response.IsSuccessStatusCode)
{
var responseContent = response.Content;
var body=await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
else
{
Console.WriteLine($"Oops! {response.StatusCode} - {response.ReasonPhrase}");
}
}
}
Explanation
In cases like this it's very important to know what's actually being sent. To do that, one can use a debugging proxy like Fiddler or Charles.
Curl with -d sends unencoded data. This call :
curl -XPOST -d 'Metadata/Type = "sas"' http://bms.org/bcknd/republish
will send :
POST http://www.google.com/bcknd/republish HTTP/1.1
Host: www.google.com
User-Agent: curl/7.55.1
Accept: */*
Connection: Keep-Alive
Content-Length: 21
Content-Type: application/x-www-form-urlencoded
Metadata/Type = "sas"
/ and " would have been replaced with other characters if URL encoding was applied. Note also the User-Agent and Accept headers
If --data-urlencode is used, the value will be URL encoded :
POST http://www.google.com/bcknd/republish HTTP/1.1
Host: www.google.com
User-Agent: curl/7.55.1
Accept: */*
Connection: Keep-Alive
Content-Length: 27
Content-Type: application/x-www-form-urlencoded
Metadata/Type =%20%22sas%22
This code on the other hand :
static async Task Main(string[] args)
{
var client=new HttpClient();
var data = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("Metadata/Type", "\"sas\""), });
var response = await client.PostAsync("http://www.google.com/bcknd/republish", data);
var responseContent = response.Content;
var body=await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
}
Will send :
POST http://www.google.com/bcknd/republish HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
Host: www.google.com
Metadata%2FType=%22sas%22
To get the original payload, one can use StringContent with hand-coded content:
var data = new StringContent("Metadata/Type= \"sas\"",Encoding.UTF8,"application/x-www-form-urlencoded");
The request is :
POST http://www.google.com/bcknd/republish HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 19
Host: www.google.com
Metadata/Type= "sas"
If you want to send the User-Agent and Accept headers, you can add them to each individual message or as default request headers :
var client=new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("curl","7.55.1"));
These will add :
Accept: */*
User-Agent: curl/7.55.1
to the request
You can call remote URL as following using HttpClient
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "http://bms.org/bcknd/republish"))
{
request.Content = new StringContent("Metadata/Type = \"sas\"", Encoding.UTF8, "application/x-www-form-urlencoded");
var response = await httpClient.SendAsync(request);
}
}
Here I have just added reference code, by using that you can create your own. I checked your curl request and it seems issue it self.

Trouble connecting with HttpClient when credentials are correct

I am in a situation where I know I can connect to an endpoint (using Postman chrome app) but I get an authentication error when I attempt it through HttpClient executing as WebJob on Azure.
public string ScanEndPoint()
{
string result;
using (var client = new HttpClient())
{
var requestContent = new MultipartFormDataContent();
var url = $"{Host}/{Path}";
requestContent.Add(new StringContent("*"), Version);
requestContent.Add(new StringContent("***"), Reference);
requestContent.Add(new StringContent("********"), Password);
var response = client.PostAsync(url, requestContent).Result;
result = response.Content.ReadAsStringAsync().Result;
}
return result;
}
The MultipartFormData is because I have to post the credentials in the body and not as headers. Clicking on the code link in Postman shows:
POST /*************.php HTTP/1.1
Host: *****-*******.****.******
Cache-Control: no-cache
Postman-Token: b574e803-1873-d7dd-ff10-bfc509991342
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="*"
**
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="***"
****
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="*********"
********************************
------WebKitFormBoundary7MA4YWxkTrZu0gW--
What steps do I need to take to replicate that postman request so that it works in code?
This is what we had to do to get it working:
using (var client = new HttpClient(handler: clientHandler, disposeHandler: true))
{
client.BaseAddress = new Uri(Host);
var url = $"{Path}";
var parameters = new Dictionary<string, string> {
{ "V", Version },
{ "ref", Reference },
{ "password", Password }
};
var encodedContent = new FormUrlEncodedContent(parameters);
var response = client.PostAsync(url, encodedContent).Result;
result = response.Content.ReadAsStringAsync().Result;
}
return result;
The handler sets up a proxy for local debug which should be irrelevant on Azure but I can remove it if that proves to be wrong.
Most of the posts I read about this suggested the same approach as #Jayendran pointed to. Any ideas on what the differences might be?
Note: we also had to re-arrange the Host and path so that the Host ended with a "/"

Xamarin.Android: HttpClient with MultipartFormDataContent returns 400 Bad Request

I develop a Xamarin.Android app and I need to update an audio recorded file.
For this, my client has provided me an API like this:
URL: /api/recordings/
Method: POST
Data format: multipart/form-data
Fields: some text fields and the audio file to send
When I test it on Postman, it works fine. The generate code snippets are:
POST /api/recordings/ HTTP/1.1
Host: X.X.X.X
Cache-Control: no-cache
Postman-Token: xxxxx-xxxx-xxxx-xxxx-xxxxx
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="param1"
param1 name
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="param2"
param2 name
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="audio_file"; filename="xxx.wav"
Content-Type: audio/wav
But when I try from the app, I get an 400 - Bad Request error.
First attempt:
I first tried with the following basic code:
using (HttpClient client = new HttpClient())
{
try
{
MultipartFormDataContent formData = new MultipartFormDataContent();
formData.Add(new StringContent("param1 name"), "param1");
formData.Add(new StringContent("param2 name"), "param2");
var response = await client.PostAsync(url, formData);
Logger.Log("Response : " + responseObj.ToString());
}
catch (Exception ex)
{
Logger.Log("Exception : " + ex.ToString());
}
}
But there is a 400 - Bad Request error:
Response : StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Server: nginx/1.15.0
Date: Mon, 25 Jun 2018 23:25:41 GMT
Connection: keep-alive
Vary: Cookie
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Content-Length: 85
Allow: POST, OPTIONS
}
Second attempt:
Then I've tried the following snippet that I found there:
string servResp = "";
string boundary = "----CustomBoundary" + DateTime.Now.Ticks.ToString("x");
using (var content = new MultipartFormDataContent(boundary))
{
content.Headers.Remove("Content-Type");
content.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=" + boundary);
content.Add(new StringContent("param1 name"), "param1");
content.Add(new StringContent("param2 name"), "param2");
HttpClientHandler handler = new HttpClientHandler();
var cookieContainer = new CookieContainer();
handler.CookieContainer = cookieContainer;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.ExpectContinue = false;
request.Content = content;
try
{
var httpClient = new HttpClient(handler);
HttpResponseMessage response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
servResp = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
Logger.Log("PostTest() - Exception : " + ex.ToString());
}
}
return servResp;
But there is still a 400 - Bad Request error:
Exception : System.Net.Http.HttpRequestException: 400 (Bad Request)
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode () [0x0002a] in <996a681f30a44cd685a4da54e11956e2>:0
=> Would you have any explanation? How could I fix this?
Edit : the audio file
I haven't specified the code of the audio file, I use a ByteArrayContent in both cases:
byte[] b = System.IO.File.ReadAllBytes(fullFilePath);
formData.Add(new ByteArrayContent(b, 0, b.Length), "audio_file", "audio.wav");
Conclusion:
I was able to resolve my problem by creating a console project that using the same service: this allows me to find and fix my error.
For an other issue, I followed the Fiddler instructions to parameter proxy on the device. However, it seems that the proxy doesn't work for apps but only for websites in the latest versions of Android...
Do you know an alternative to Fiddler that could work on Android devices?

Categories

Resources