SendAsync using HttpClient in Xamarin Forms not awaiting - c#

I am busy developing a Xamarin forms application and have been struggling the last 2 days to get a successful post to my webApi from the application. Posting to the WebApi from the chrome Postman app works perfectly but I cannot seem to get it going from the application.
This is the webApi method that I am trying to call:
[HttpPost]
public string Authenticate(HttpRequestMessage request)
{
string JsonObj = request.Content.ReadAsStringAsync().Result;
AuthToken _authToken = JsonConvert.DeserializeObject<AuthToken>(JsonObj);
int UserID = DomainAuth.ValidateDomainAccount(_authToken.Username, _authToken.Password);
if(UserID > 0)
{
_authToken.UserID = UserID;
_authToken.Authenticated = true;
}
else
{
switch(UserID)
{
case -99:
_authToken.AuthMessage = "The entered domain account does not exist";
break;
case -98:
_authToken.AuthMessage = "The entered domain account does not have application access";
break;
case -97:
_authToken.AuthMessage = "Incorrect username and password combination";
break;
}
}
return JsonConvert.SerializeObject(_authToken);
}
I was originally trying to post to IIS Express but read on a post that this cannot be done when using an external device so the api is hosted in IIS now.
This is the method that my viewmodel command calls:
public async void Login()
{
Tuple<bool, string> AuthCheck = await _authService.AuthenticateDomainAccount(_username, _password);
if (AuthCheck.Item1) //Item1: True / False value indicating if the login was a success
{
Helpers.Settings.Username = _username;
await _navigationService.NavigateAsync("Synchronization");
}
else
{
FeedbackMsg = AuthCheck.Item2; // Item2: Message indicating why the login has failed.
}
}
And lastly this is the method that makes the api call:
public async Task<Tuple<bool, string>> AuthenticateDomainAccount(string _Username, string _Password)
{
AuthToken _authToken = new AuthToken { Username = _Username, Password = _Password };
HttpClientHandler handler = new HttpClientHandler();
using (var client = new HttpClient(handler, false))
{
client.BaseAddress = new Uri(BaseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "User/Authenticate"))
{
req.Content = new StringContent(JsonConvert.SerializeObject(_authToken), Encoding.UTF8, "application/json");
try
{
var response = await client.SendAsync(req);
Debug.WriteLine("################# HERE I AM ################");
var result = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
string a = "a";
}
}
}
return null;
}
When I step through the application it hits the SendAsync line, but instead of waiting for it to return as expected, it skips the lines below it and immediately returns null as per the final line of the method.
Can anyone shed some light on this?

I think the issue over here is with the Uri you are trying to hit. SendAsync method may be throwing a 404 because of incorrect Url.
Please change the following line:
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "User/Authenticate"))
to
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "api/User/Authenticate"))
assuming your BaseAddress ends with a '/' at the end, else it will be
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "/api/User/Authenticate"))

Related

C# GetAsync await returns Status code 204 Whereas PostMan returns 200

I have a Get API call to WebAPI using GetAsync method that fetches some values in JSON based on a location parameter. I am running the WebAPI call in a loop and for some reason, for certain Location parameters, the code returns "No content" status code 204. When I call the webAPI from Postman, it returns the data successfully.
Here is my code:
private static async Task<string[]> GetLatLongValues(string locationDescription)
{
string[] latLng = new string[2];
string locationAPI = "https://zoek-search-api-live-us-2.azurewebsites.net/" ;
try
{
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(locationAPI);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("cache-control", "no-cache");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//HTTP GET
var responseTask = await client.GetAsync("Geocode/SearchLocation?location=" + Uri.EscapeDataString(locationDescription)); //.ConfigureAwait(continueOnCapturedContext: false);
responseTask.EnsureSuccessStatusCode();
if (responseTask.IsSuccessStatusCode)
{
var resString = await responseTask.Content.ReadAsStringAsync ();
//resString.Wait();
if (resString.Length !=0)
{
Newtonsoft.Json.Linq.JArray resArr = Newtonsoft.Json.Linq.JArray.Parse(resString);
latLng[0] = resArr[0]["latitude"].ToString();
latLng[1] = resArr[0]["longitude"].ToString();
validLatLong.Add(locationDescription, latLng);
}
else
{
latLng[0] = "0";
latLng[1] = "0";
InvalidLatLong.Add(locationDescription, latLng);
isLocValid = false;
}
}
}
return latLng;
}
catch(Exception ex)
{
latLng[0] = "0.0";
latLng[1] = "0.0";
return latLng;
}
}

Unable to send string as HTTPContent

I have created an API, which shall have the capability to connect to en external API via POST and with a request body in form of a string.
I am able to connect directly to the API from Postman without trouble.. But it does not work via my own API.
Any ideas?
This is the Pastebin.
private string EncodeExternalApiLink = "https://blabla.dk";
private string EncodeExternalApiLinkPostFilter = "searchstring/blabla/api/search";
[HttpPost("getdata/filtered")]
public async Task<IActionResult> GetDataFromExternalFiltered([FromBody] string filter)
{
var filterString = new StringContent(filter);
EncodeExternalToken token = GetExternalToken().Result;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(EncodeExternalApiLink);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.access_token);
using (var response = await client.PostAsync(EncodeExternalApiLinkPostFilter, filterString))
{
return Json(response);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
return Content(content, "application/json");
}
else
{
return NotFound();
}
}
}
}
Salutations. You might need to add a "/" to the end of your base address EncodeExternalApiLink or to the beginning of EncodeExternalApiLinkPostFilter.

HttpClient call keeps on waiting mode but works fine in Console App

Below is the code which works perfectly fine when i execute it on console application.
The line
var postResponse = await client.SendAsync(req); give the result when i run the code in console app.
But when iam using this code in WebApi controller, this code halts on this line.
using (var client = new HttpClient())
{
var auth = "MTAwNDgucnVsZXNlbmdpbmUuc2VydmljZTp2N3FuY3I4cWlz";
client.BaseAddress = new Uri("https://federation-sts-stage.accenture.com");
var req = new HttpRequestMessage(HttpMethod.Post, "https://federation-sts-stage.test.com/oauth/ls/connect/token");
var cont = new FormUrlEncodedContent(bodyContents);
cont.Headers.ContentType = new MediaTypeHeaderValue("application/json");
cont.Headers.ContentLength = Convert.ToInt64("125");
req.Content = cont;
req.Headers.Add("Authorization", "Basic " + auth);
try
{
var postResponse = await client.SendAsync(req); // this is where the code keeps on waiting but works fine in console app
postResponse.EnsureSuccessStatusCode();
responseContents = postResponse.Content.ReadAsStringAsync().Result;
}
catch (Exception ex)
{
var msg = ex.Message;
return msg;
}
var responseObject = JObject.Parse(responseContents);
return responseObject.Value<string>("access_token");
}
I have also compared the request object in both the cases (in console app and in webapi controller call) but in both the cases the request object comes out same as below :
{Method: POST, RequestUri: 'https://federation-sts-stage.test.com/oauth/ls/connect/token', Version: 1.1, Content: System.Net.Http.FormUrlEncodedContent, Headers:
{
Authorization: Basic MTAwNDgucnVsZXNlbmdpbmUuc2VydmljZTp2N3FuY3I4cWlz
Content-Type: application/json
Content-Length: 125
}}
I dont know what iam doing incorrect.
As per the comments, i am putting the whole method which gets called from apicontroller as below, this method works fine from console app but when i call this method from apicontroller its kept on running.
public async Task<string> RequestTokenFromIssuer(string username, string password)
{
var bodyContents = new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "userName", username },
{ "password", password},
{ "scope", "read_rulesengine write_rulesengine" }
};
string responseContents = string.Empty;
using (var client = new HttpClient())
{
var auth = "MTAwNDgucnVsZXNlbmdpbmUuc2VydmljZTp2N3FuY3I4cWlz";
client.BaseAddress = new Uri("https://federation-sts-stage.test.com");
var req = new HttpRequestMessage(HttpMethod.Post, "https://federation-sts-stage.test.com/oauth/ls/connect/token");
var cont = new FormUrlEncodedContent(bodyContents);
cont.Headers.ContentType = new MediaTypeHeaderValue("application/json");
cont.Headers.ContentLength = Convert.ToInt64("125");
req.Content = cont;
req.Headers.Add("Authorization", "Basic " + auth);
try
{
var postResponse = await client.SendAsync(req);
postResponse.EnsureSuccessStatusCode();
responseContents = postResponse.Content.ReadAsStringAsync().Result;
}
catch (Exception ex)
{
var msg = ex.Message;
return msg;
}
var responseObject = JObject.Parse(responseContents);
return responseObject.Value<string>("access_token");
}
}
I believe your problem actually is on line
responseContents = postResponse.Content.ReadAsStringAsync().Result;
Never, ever to this in an async method. You should do it like:
responseContents = await postResponse.Content.ReadAsStringAsync();

WebAuthenticationBroker Issues

I having some issues with the data being returned by the WebAuthenticationBroker in my app. The api I'm connecting to utilizes OAuth 1.0a. I'm able to get the Broker to pull up the authorization page, but ResponseData from the Broker is just url to the page that contains the key value pair for the OAuth Verifier. So I tried a web request to try and get that data, but end up getting 405: Method Not allowed error from the page. Here is my code...
public async Task Authorize(string oAuthAuthorizeUrl)
{
// Start WebAuthenticationBroker
System.Uri StartUri = new Uri(oAuthAuthorizeUrl);
System.Uri EndUri = new Uri(oAuthCallBack);
WebAuthenticationResult auth = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, StartUri, EndUri);
if (auth.ResponseStatus == WebAuthenticationStatus.Success)
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync(auth.ResponseData);
HttpContent content = response.Content;
string responseText = await content.ReadAsStringAsync();
string oauth_verifier = null;
string[] keyValPairs = responseText.Split('&');
for (int i = 0; i < keyValPairs.Length; i++)
{
String[] splits = keyValPairs[i].Split('=');
switch (splits[0])
{
case "oauth_verifier":
oauth_verifier = splits[1];
break;
}
}
oAuthVerifier = oauth_verifier;
}
}
is this how the Broker is supposed to work? I assumed it would send back the data with the Verifier. Thanks for the help.

Access cloudant db using .Net HttpClient

I am attempting to connect to Cloudant (a couch-style DB) from a .Net MVC application. I am following the guidelines for consuming a web API using the HttpClient, as illustrated here:
http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client
I have two methods so far -- one to get a document and one to create a document -- and both have errors. The Get method returns Unauthorized and the Post method returns MethodNotAllowed.
The client is created like this:
private HttpClient CreateLdstnCouchClient()
{
// TODO: Consider using WebRequestHandler to set properties
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(_couchUrl);
// Accept JSON
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
The Get method is:
public override string GetDocumentJson(string id)
{
string url = "/" + id;
HttpResponseMessage response = new HttpResponseMessage();
string strContent = "";
using (var client = CreateLdstnCouchClient())
{
response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
strContent = response.Content.ReadAsStringAsync().Result;
}
else
{
// DEBUG
strContent = response.StatusCode.ToString();
LslTrace.Write("Failed to get data from couch");
}
}
return strContent;
}
The Post method is:
public override string CreateDocument(object serializableObject)
{
string url = CouchApi.CREATE_DOCUMENT_POST;
HttpResponseMessage response = new HttpResponseMessage();
string strContent = "";
using (var client = CreateLdstnCouchClient())
{
response = client.PostAsJsonAsync(url, serializableObject).Result;
strContent = response.Content.ReadAsStringAsync().Result;
}
if (response.IsSuccessStatusCode)
{
return strContent;
}
else
{
LslTrace.Write("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
return response.StatusCode.ToString();
}
}
URLs are per the API documentation: https://username:password#username.cloudant.com.
I am very confused by what is going on and having a lot of trouble finding examples. Thanks for your help!
Thomas
With the HttpClient, you need to do the following to authenticate correctly (assuming you use basic auth):
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential(_userName, _password);
HttpClient client = new HttpClient(handler) {
BaseAddress = new Uri(_couchUrl)
};
You should not specify the username/password in the _couchUrl - HttpClient doesn't support that.
I can't see your implementation of PostAsJsonAsync or the complete Url your are building, but you can try inspecting / logging response.ReasonPhrase when an error occurs to get a hint as to what went wrong.

Categories

Resources