Xamarin Forms ios http request header is always null - ios

I am working on a mobile app using xamarin forms, the application needs to get some data from an api so i use http client to implement that.
so, the http requests work fine on both Android and Windows Phone, but when i try to debug the request on IOS i get an error which of course doesn't show an exception details.. but while debugging i found that the error happens while encoding the contents of the request, so it returns NULL in the IOS case, while it returns the encoded content in both android and windows phone case.
I used different ways to send the resquest and to encode the request, but got the same result.
Here are my trials:
the http request code1:
var values = new Dictionary<string, string>
{
{ "ExpiryDate", dateAndTime.ToString() },
{ "IsHijri", "false" },
{ "CR_No", CetNum.Text }
};
var content = new FormUrlEncodedContent(values);
var response = await client2.PostAsync("https://XXXXXXX.com/api/XXXXXX", content);
and this is the request code trial 2 :
var myContent = JsonConvert.SerializeObject(cert);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var Response = await client.PostAsync("api/PlaPla/PlaPla",
byteContent).ConfigureAwait(false);
string res = await Response.Content.ReadAsStringAsync();
Those trials work fine and get the content encoded with other platforms, but not IOS.
the error message i get is System.NullReferenceException: Object
reference not set to an instance of an objec
So, is there a different way i should be using to send an http request in the ios scenario ? any help ? Thanks.

Related

Order of object properties is changed while posting data from ionic native http client

I am working on ionic app which will be used on android and iOS platforms. App uses one endpoint to post user data to the backend. Data posted on a backend is as follows:
{
"name": "Citizen Foo",
"emailAddress": "citizen.foo#gmail.com",
"role": "citizen"
}
For security purpose every request which is being sent is validated. In order to do this client sends authorization header with every request. Backend creates one for every request and matches it with one sent by client and then only responds otherwise throw an exception. For creating the authorization header, data sent over post request is also a part of the logic. I have simplified this logic becuase the actual problem is different but this part is important to understand the problem.
Following is the sample code on client side:
var sRequestBody = JSON.stringify(data);
var requestBodyBytes = this.getByteArray(unescape(encodeURIComponent(sRequestBody)));
var authHeader = md5.base64(requestBodyBytes);
var headers = {};
headers['Authorization'] = authHeader;
this.nativeHttp.setDataSerializer('json');
this.nativeHttp.clearCookies();
this.nativeHttp.setSSLCertMode('nocheck');
return Observable.fromPromise(this.nativeHttp.post(url, data, headers));
Then, backend which is asp.net web api, calculates authorization in the same way and matches it with one send by client and gives the response if it matches.
var rawContent = await content.ReadAsByteArrayAsync();
var stringContent = content.ReadAsStringAsync().Result;
var hash = md5.ComputeHash(rawContent);
var authorization = Convert.ToBase64String(hash);
if (authHeader != authorization)
throw;
When the above call is made from android,
value of dataString on client is
"{"name":"Citizen Foo","emailAddress":"citizen.foo#gmail.com","role":"citizen"}"
value of stringContent on server is
"{""name"":""Citizen Foo"",""emailAddress"":""citizen.foo#gmail.com"",""role"":""citizen""}"
and it allows request coming from android app.
When the same is run on iOS and the post user call is made,
Value of dataString on client is
"{"name":"Citizen Foo","emailAddress":"citizen.foo#gmail.com","role":"citizen"}"
Value of stringContent on server is
"{""name"":""Citizen Foo"",""role"":""citizen"",""emailAddress"":""citizen.foo#gmail.com""}"
and it does not allow requst coming from iOS app.
And the only reason it is happening is because of the way user object is serialized/received at the backend when request is made from iOS. Order of properties while sending is name,emailAddress,role. While it is received with order name,role,emailAddress. Thus, the authorization value calculated on server side is different than authHeader coming from client and the call is terminated.
stringContent was added on server side just to debug and understand what is being received at server. Order of the object properties on client is different than object properties received on server side. Is there a way to maintain the order of object properties when the request is made from iOS platform? Direction in any way to solve this problem is appreciated.
One solution that worked for us is this:
this.httpClient.setDataSerializer('utf8');
response = await this.httpClient.post(url, JSON.stringify(postBody), {'Content-Type': 'application/json'});
So key points here are:
utf8 as data serializer
body has to be stringified
header 'Content-Type': 'application/json' has to be set

UWP POST request like GET

I need to send POST request from my UWP app.
I read about it here.
I use one shared HttpClient.
private Windows.Web.Http.HttpClient httpClient;
Initialization:
httpClient = new Windows.Web.Http.HttpClient();
var headers = httpClient.DefaultRequestHeaders;
string header = "Chrome/64.0.3282.140";
if (!headers.UserAgent.TryParseAdd(header))
{
throw new Exception("Invalid header value: " + header);
}
I use this object for all request
But when I use it for POST request, it works like GET request or POST, but without parameters
Uri requestUri = new Uri("http://some_websit.ru");
Dictionary<string, string> pairs = new Dictionary<string, string>();
pairs.Add("par1", "val1");
pairs.Add("par2", "val2");
HttpFormUrlEncodedContent formContent = new HttpFormUrlEncodedContent(pairs)
var result = await httpClient.PostAsync(requestUri, formContent);
string resultContent = await result.Content.ReadAsStringAsync();
It ignore parameters which I give.
I tried to send POST request here http://seriyps.ru/postget/ and it works.
There is nothing wrong with your code, I have tested it locally with the same code, only a different URL, and the POST request is sent properly along with the passed in parameters:
I recommend you to install Telerik Fiddler 4 to see the network traffic and confirm that the parameters are indeed sent. I have used http://example.com just as a sample URL here. I would suspect the problem is rather on the side of the server than your app or that the server expect different parameters than what is being sent.

Simple POST from website to external web api fails

I have an asp .net MVC-page
Im trying to connect to Eventbrite:s api
Simply put, it requires you to send client id to one url, using HttpGET and HttpPOST the result and some more info to another url.
The GET goes fine and I get the required (auth)"code". When I try to make the POST to the second url I get
"Socket Exception: An existing connection was forcibly closed by the
remote host"
I can POST to the second url, using Postman and the info from the GET-request it works ok, I get the auth token just fine.
This is the code Im using
var parameters = new Dictionary<string,string>();
parameters.Add("code", pCode);
parameters.Add("client_secret", CLIENT_SECRET);
parameters.Add("client_id", CLIENT_APP_KEY);
parameters.Add("grant_type", "authorization_code");
using (var client = new HttpClient())
{
var req = new HttpRequestMessage(HttpMethod.Post, pUrl) { Content = new FormUrlEncodedContent(parameters) };
var response = client.SendAsync(req).Result;
return response.Content.ReadAsStringAsync().Result;
}
I have a vague memory of a similar problem when publishing to Azure. Since I have to register my app with a public return url I cant look at the request with fiddler.
My site is running https.
I have also tested adding the below line (from some googling)
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
But then I get 404-error...
I have also tested this (with same result)
using (var client = new HttpClient())
{
var response = client.PostAsync(pUrl, content).Result;
authToken = response.Content.ReadAsStringAsync().Result;
}
Ive tested getting the auth code and and running the POST from local machine, same result...
I have contacted eventbrite developer support to see if they can help me as well...
This POST must contain the following urlencoded data, along with a Content-type: application/x-www-form-urlencoded header.
Since your content-type is application/x-www-form-urlencoded you'll need to encode the POST body, especially if it contains characters like & which have special meaning in a form.
Then use the following function to post your data:
using (var httpClient = new HttpClient())
{
using (var content = new FormUrlEncodedContent(parameters))
{
content.Headers.Clear();
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
HttpResponseMessage response = await httpClient.PostAsync(url, content);
return await response.Content.ReadAsAsync<TResult>();
}
}
The error message you provided means the remote side closed the connection, the causes are:
·You are sending malformed data to the application.
·The network link between the client and server is going down for some reason.
·You have triggered a bug in the third-party application that caused it to crash.
·The third-party application has exhausted system resources.
·Set ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
For more details, you could refer to this case.
Changed OAuth access token URL from
https://www.eventbrite.com/oauth/token/
to (as specified)
https://www.eventbrite.com/oauth/token
(ie without trailing slash). Now it works

AuthFlow.CreateCredentialsFromVerifierCode throws error only on load balancer

I am using Tweetinvi for posting images to Twitter.
From our App servers its working fine to post to Twitter.
But, When tried from our load balancer getting this error -
Error:The credentials are required as the URL does not contain the
credentials identifier.
Stack Trace: at Tweetinvi.AuthFlow.CreateCredentialsFromVerifierCode(String
verifierCode, String authorizationId, IAuthenticationContext
authContext)
My code snippet is like this -
var verifierCode = Request.Params.Get("oauth_verifier");
var authorizationId = Request.Params.Get("authorization_id");
var userCreds = AuthFlow.CreateCredentialsFromVerifierCode(verifierCode, authorizationId);
I see these parameters(oauth_verifier, authorization_id,..) being passed to the callback page. But still seeing the above error in the call back page.
Note: this issue is only when I try posting to Twitter on our loadbalancer (using the individual servers working fine).
Should I use a different overloaded function?
So the problem comes from the fact that you are actually using a load balancer. But let me explain how the authentication works and how you can solve your problem.
var appCredentials = new ConsumerCredentials("", "");
var authContext = AuthFlow.InitAuthentication(appCredentials, "");
When you call AuthFlow.InitAuthentication, it returns an IAuthenticationContext. This context contains all the information required to process the callback from Twitter.
But in addition to this, Tweetinvi adds a parameter authorization_id to the callback so that it can map the callback request to an actual IAuthenticationContext.
var authorizationId = Request.Params.Get("authorization_id");
var userCreds = AuthFlow.CreateCredentialsFromVerifierCode(verifierCode, authorizationId);
When you call AuthFlow.CreateCredentialsFromVerifierCode with an authorization_id as a parameter it will look into the local dictionary and try to get the IAuthenticationContext.
Because you are using a load balancer, the server executing the AuthFlow.InitAuthentication can be different from the server your receiving the callback request.
Because your callback arrives at a different server, it actually result in the AuthenticationContext being null.
This is what I tried to explain in the documentation.
How to solve this?
What you need to do is to store the IAuthenticationContext information required for the CreateCredentialsFromVerifierCode to continue its work when it receives the callback. I would suggest you store this in your database.
When you receive your callback you will have to get back these information from your db. To do that I would suggest that when you initally call the `` you add to the callback url a parameter with the value storing the authentication id in your database (e.g. my_auth_db_id=42).
var authContext = AuthFlow.InitAuthentication(appCredentials, "http://mywebsite.com?my_auth_db_id=42");
When your callback arrives you will be able to do :
var myDBAuthId = Request.Params.Get("my_auth_db_id");
With this value you can now create a new token with the required information (stored in the db).
var token = new AuthenticationToken()
{
AuthorizationKey = "<from_db>",
AuthorizationSecret = "<from_db>",
ConsumerCredentials = creds
};
Now you are ready to complete the operation:
var userCreds = AuthFlow.CreateCredentialsFromVerifierCode(verifierCode, token );
I realize this is a big post, but I wanted to explain how it works.
Please let me know if anything does not makes sense.

ArcGis Connection Error using RestSharp

I'm trying to connect to ArcGis to geocode an address to a lat/long, from an Xamarin.iOS app. I'm initiating the request using this manner:
var request = new RestRequest ("arcgis/rest/services/World/GeocodeServer/find?f=pjson&text=380+New+York+Street+Redlands+CA+92373", Method.POST);
request.AddParameter ("content-type", "application/json");
var client = new RestClient("http://geocode.arcgis.com/");
var response = client.Execute (request);
var jsonOutput = response.Content;
However, it always returns blank, and gives me the error "Invalid Cookie Domain: geocode.arcgis.com". Why is this error happening? I can't figure this out...
I have put an ArcGIS component on the Xamarin store (called ArcGIS.PCL) which demonstrates calling this operation in the sample projects if you want to try that (it's free).
I think your issue comes down to the underlying http client connection as using something like the modernhttpclient seems to prevent that error occurring.

Resources