HttpWebRequest to NSUrlConnection/NSUrlSession or HttpClient (with ModerHttpClient lib) - c#

We have code written in Xamarin Cross Platform that works for Android with Clients tunneling software:
string body = "<rest_access/>";
byte[] dataByte = Encoding.ASCII.GetBytes(body);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(endpoint + #"/rest_access");
request.Method = "POST";
request.ContentType = "application/xml";
request.Accept = "application/json";
var credentials = System.Text.Encoding.ASCII.GetBytes(username + ":" + password);
string encodedCredentials = System.Convert.ToBase64String(credentials);
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + encodedCredentials);
request.ContentLength = dataByte.Length;
Stream stream = request.GetRequestStream();
stream.Write(dataByte, 0, dataByte.Length);
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
string responseString = reader.ReadToEnd().ToString();
dynamic authResponse = JsonConvert.DeserializeObject(responseString);
//Console.WriteLine("Auth response:\n" + authResponse);
accessKey = authResponse.rest_access.access_key;
//Console.WriteLine("kljuc " + accessKey);
return accessKey;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return "Unexpected Error";
}
But when using this same code for iOS application tunnel does not seem to be present.
In their documentation we found:
Apps built with the Xamarin development platform can access network servers in various ways. AppTunnel with
HTTP/S tunneling is supported only as follows:
• The app uses the NSURLConnection or NSURLSession APIs exposed to C# through the Xamarin.iOS
binding.
• The app uses the ModernHttpClient library with NSURLSession. The ModernHttpClient library with
CFNetwork will not work.
For example, the app initializes the instance of the ModernHttpClient as follows:
var httpClient = new HttpClient (new NativeMessageHandler ());
Does this mean that we have to rewrite all methods that used HttpWebRequest in them to now use one of these libs?
If so could I get some link to How to rewrite these so they are acceptable?

Have you tried to set the HttpClient Implementation for Xamarin.iOS in
HttpClient Stack
change it to NSUrlSession or Managed

Related

C# HTTP request 401 and 500 error

I've been working on the Walmart API but I keep getting either the 401 error or the 500 error when I run the code
public void post()
{
byte[] data = Encoding.ASCII.GetBytes(
$"username={user}&password={password}");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://marketplace.walmartapis.com/v2/feeds?feedType=item");
request.Method = "POST";
request.Accept = "application/xml;";
request.ContentLength = data.Length;
request.Headers.Add("WM_SVC.NAME", "Walmart Marketplace");
request.Headers.Add(authId);
request.Headers.Add("WM_CONSUMER.ID", user);
request.Headers.Add( time);
request.Headers.Add(CorId);
using (Stream stream = request.GetRequestStream ())
{
stream.Write(data , 0, data.Length);
}
string responseContent = null;
using (WebResponse response = request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
using (StreamReader sr99 = new StreamReader(stream))
{
responseContent = sr99.ReadToEnd();
}
}
}
MessageBox.Show(responseContent);
}
where authID is a signature generated from a jar file provided by walmart
time is also generated from the jar file
CorID is a randomly generated number
and user is the user id.
here is the link that describes the header parameters. Did I miss something in my header?
https://developer.walmartapis.com/#getting-started
There are multiple problems with your request. First, you are submitting a feed, but sending it as an application/xml when it should be a multipart/form-data request. Beyond this, your headers aren't set up properly and there is currently a major problem with submitting multipart/form-data requests to Walmart using C#. I have not seen a post from anyone successfully sending a feed to Walmart via C#. I am currently using C# to execute a batch file that then fires a modified version of the Walmart Java SDK which is capable of sending the multipart/form-data requests.
The reponse below is for any request other than feeds. I would start with the example listed below to get familiar with how you need to set your headers up. This is going to work for the majority of Walmart interfacing, but if the request is a feed style request, you will either need to come up with a better solution to the multipart/form-data issue, use the Java SDK, or wait for the C# SDK. If someone reads this and has a better answer as to how to submit feeds via C# exclusively I would love to hear about it!
Here is an example of an application/xml request that works.
string timestamp = CurrentTimeMillis().ToString().Trim();
string query = #"orders/"+poID+"/acknowledge";
string request = v3BaseUrl + query; //Constructed URI
string stringToSign = consumerId + "\n" +
request.Trim() + "\n" +
"POST" + "\n" +
timestamp + "\n";
string signedString = signData(stringToSign); //Your signed string
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(request);
webRequest.Accept = "application/xml";
webRequest.ContentType = "application/xml";
webRequest.Method = "POST";
webRequest.Headers.Add("WM_SVC.NAME", "Walmart Marketplace");
webRequest.Headers.Add("WM_SEC.AUTH_SIGNATURE", signedString);
webRequest.Headers.Add("WM_CONSUMER.ID", consumerId);
webRequest.Headers.Add("WM_SEC.TIMESTAMP", timestamp.ToString().Trim());
webRequest.Headers.Add("WM_QOS.CORRELATION_ID", Guid.NewGuid().ToString());
webRequest.Headers.Add("WM_CONSUMER.CHANNEL.TYPE", channelType);
webRequest.ContentLength = 0;
webRequest.Timeout = Timeout.Infinite;
webRequest.KeepAlive = true;
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
success = true;
}
}

Connectiong to API with C# NetworkingClient

Can someone help me with following API connection with C#. Never done any API connections before so i am a little unsure how it work. This is for a universal windows application so it will be using c# and XMAL.
Is it possible to do the following PHP API call below using C#:
<?php
$uri = 'http://api.football-data.org/v1/soccerseasons/354/fixtures/?matchday=22';
$reqPrefs['http']['method'] = 'GET';
$reqPrefs['http']['header'] = 'X-Auth-Token: YOUR_TOKEN';
$stream_context = stream_context_create($reqPrefs);
$response = file_get_contents($uri, false, $stream_context);
$fixtures = json_decode($response);
?>
Basically all i need to know is if it is even possible.
Thanks in advance.
It can be something like this (result is as text but you can json too):
HttpWebRequest request =(HttpWebRequest)WebRequest.Create("http://api.football-data.org/v1/soccerseasons/354/fixtures/?matchday=22");
request.Headers.Add("AUTHORIZATION", "Basic YTph");
request.ContentType = "text/html";
request.Method = "GET";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader stream = new StreamReader(response.GetResponseStream());
string ResultAsText = stream.ReadToEnd().ToString();
Here is my favorite example of calling a WEB API: http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
It shows how to use HttpClient, uses async / await and introduces Formatters.

Can a C# console app call a WEB API using classes from .Net 3.5?

How can I re-write the console app below using classes from .NET 3.5? What classes should I be looking into?
The console application needs to be compiled with .net 3.5 and cannot use the HttpClient class.
And it needs to be calling an WEB API and pass an XML string as I am doing below:
string xml = "<test>blah</test>";
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://CONTOSO/");
HttpContent xmlContent = new StringContent(xml);
HttpResponseMessage response = client.PostAsync("API/Import/", xmlContent).Result;
if (response.IsSuccessStatusCode)
{
Console.WriteLine(String.Format("Success."));
}
else
{
Console.WriteLine("{0: } {1}", (int)response.StatusCode, response.ReasonPhrase);
}
I tried the code below but I get error: "ERROR: (500) Internal Server Error.":
string baseAddress = "http://CONTOSO/";
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(baseAddress + "API/Import");
req.Method = "POST";
req.ContentType = "text/xml";
Stream reqStream = req.GetRequestStream();
string fileContents = xml;
byte[] fileToSend = Encoding.UTF8.GetBytes(fileContents);
reqStream.Write(fileToSend, 0, fileToSend.Length);
reqStream.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); //<== "ERROR: (500) Internal Server Error."
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
RestSharp does support .NET 3.5,
https://github.com/restsharp/RestSharp
Thus, you might use this open source HTTP client library to consume Web API.

Making (POST) signed requests to AWS IAM using C#

I am trying follow the example given at AWS documentation Signing AWS requests and make a ListUsers call in C#. I have arrived till the last stage of generating the signature (i.e ready to submit the signed request given at signature-4 request examples). But the code I pasted below is throwing 'bad request' exception when submitted.
static void submitSignedRequest(string my_access_key, string my_secret_key, string signature, string curr_utc_date_string, string curr_utc_datetime_string)
{
string url = "https://iam.amazonaws.com/?Action=ListUsers&Version=2010-05-08&X-Amz-Algorithm=AWS4-HMAC-SHA256&" +
"X-Amz-Credential=" + my_access_key + "/" + curr_utc_date_string + "/us-east-1/iam/aws4_request&X-Amz-Date=" + curr_utc_datetime_string +
"&X-Amz-SignedHeaders=content-type;host;x-amz-date&X-Amz-Signature=" + signature;
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "POST";
request.Host = "iam.amazonaws.com";
request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
WebHeaderCollection headers = (request as HttpWebRequest).Headers;
headers.Add("Authorization", "AWS4-HMAC-SHA256 Credential=" + my_access_key +"/" + curr_utc_date_string + "/us-east-1/iam/aws4_request, " +
"SignedHeaders=content-type;host;x-amz-date, Signature=" + signature);
headers.Add("x-amz-date", curr_utc_datetime_string);
HttpWebResponse response;
try
{
response = request.GetResponse() as HttpWebResponse;
}
catch (Exception ex)
{
Console.WriteLine("Error Message: " + ex.Message);
}
}
Output i get is:
Error Message: The remote server returned an error: (400) Bad Request.
Can some one help me what wrong I am doing here?
Edit:
I solved this finally by myself. I had to transform it to below. (answer is for RDS though I believe it's visible what the differences are).
string url = "https://rds.us-west-1.amazonaws.com";
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
// ASCIIEncoding encoding = new ASCIIEncoding();
string postData = "Action=DescribeDBInstances&Version=2013-09-09";
byte[] data = Encoding.UTF8.GetBytes(postData);
request.Method = "POST";
request.Host = "rds.us-west-1.amazonaws.com";
request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
// request.ContentLength = data.Length;
using (Stream stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
WebHeaderCollection headers = (request as HttpWebRequest).Headers;
request.Headers.Add("Authorization", "AWS4-HMAC-SHA256 Credential=" + my_access_key + "/" + curr_utc_date_string + "/us-west-1/rds/aws4_request, " +
"SignedHeaders=content-type;host;x-amz-date, Signature=" + signature);
request.Headers.Add("x-amz-date", curr_utc_datetime_string);
Can some one help me what wrong I am doing here?
Maybe, but I strongly suggest to skip this endeavor all together and just use the excellent AWS SDK for .NET for all your AWS API interactions instead, because it indeed helps take the complexity out of coding by providing .NET APIs for many AWS services including Amazon S3, Amazon EC2, DynamoDB and more.
I'm working with AWS for years in various environments and with all sorts of languages, and I have never even bothered to look into using their API without the assistance of one of the multitude of SDKs they are offering, most of which offer high level tooling in addition to making working with the API from your respective language much easier in the first place.
But if you really need or want to do it yourself, I suggest to simply take a look at the source code of these very SDKs, which are all available at GitHub, including the one for the AWS SDK for .NET - regarding the issue at hand, you might want to start looking into AWS4Signer.cs for example.
Please note that AWS has just released the significantly overhauled version 2, see GA Release of AWS SDK for .NET Version 2 for details.

HTTP Basic Auth for Diigo using C#: How to read response?

I am (trying) to develop a WPF (C#) app that just gets (or at least is supposed to get) my saved bookmarks at Diigo.com profile. The only helpful page i found is this . It says i have to use HTTP Basic authetication to get my self authenticated and make requests then. But don't understand how C# handles it!. The only solution i came up with below just prints entire HTML source to console window.
string url = "http://www.diigo.com/sign-in";
WebRequest myReq = WebRequest.Create(url);
string usernamePassword = "<username>:<password>";
CedentialCache mycache = new CredentialCache();
mycache.Add(new Uri(url), "Basic", new NetworkCredential("username", "password"));
myReq.Credentials = mycache;
myReq.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword)));
//Send and receive the response
WebResponse wr = myReq.GetResponse();
Stream receiveStream = wr.GetResponseStream();
StreamReader reader = new StreamReader(receiveStream, Encoding.UTF8);
string content = reader.ReadToEnd();
Console.Write(content);
Here username and password are hardcoded but of course they'll come from some txtUsername.Text thing. And after that how am i going to read the JSON response and parse it?
What is that i need to do to get my app or myself HTTP basic authenticated?
Any help or suggestion is welcome!
If you're trying to talk to a service, you probably want to use the Windows Communication Foundation (WCF). It's designed specifically to solve the problems associated with communicating with services, such as reading/writing XML and JSON, as well as negotiating transports mechanisms like HTTP.
Essentially, WCF will save you doing all of the "plumbing" work of working with HttpRequest objects and manipulating strings. Your problems have already been solved by this framework. Use it if you can.
Ok i solved the problem after some (not really some) effort. Code below gets the JSON response from server which can then be parsed using any preferred method.
string key = "diigo api key";
string username = "username";
string pass = "password";
string url = "https://secure.diigo.com/api/v2/";
string requestUrl = url + "bookmarks?key=" + key + "&user=" + username + "&count=5";
HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(requestUrl);
string usernamePassword = username + ":" + pass;
myReq.Timeout = 20000;
myReq.UserAgent = "Sample VS2010";
//Use the CredentialCache so we can attach the authentication to the request
CredentialCache mycache = new CredentialCache();
//this perform Basic auth
mycache.Add(new Uri(requestUrl), "Basic", new NetworkCredential(username, pass));
myReq.Credentials = mycache;
myReq.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(new ASCIIEncoding().GetBytes(usernamePassword)));
//Send and receive the response
WebResponse wr = myReq.GetResponse();
Stream receiveStream = wr.GetResponseStream();
StreamReader reader = new StreamReader(receiveStream, Encoding.UTF8);
string content = reader.ReadToEnd();
Console.Write(content);
content is the JSON response returned from server
Also this link is useful for getting started with api.

Categories