How to make https webAPI call? - xamarin

I'm trying to make https webAPI call, specifically - Google Directions API. Putting the uri directly inside browser gives me the result that I want, so I'm 100% sure my uri is correct.
Now, how do I call the webapi inside my PCL? Using modernhttp and HttpClient now, but am open to whatever options there are out there.
private async Task<string> GetJsonObjFromUrl(string urlRoutes)
{
HttpClient c = new HttpClient(new NativeMessageHandler());
var resp = await c.SendAsync(new HttpRequestMessage(HttpMethod.Get, new Uri(urlRoutes)));
if (resp.IsSuccessStatusCode)
{
var json = await resp.Content.ReadAsStringAsync();
return json;
}
return null;
}
What am I doing wrong?
Edit: Just putting this here because this was driving me crazy whole night. Ends up the caller way, way above forgot to put await. The execution continues straight after and never returns to get the result. That's why I never got any results... :\

The code just don't go hit anywhere below client.SendAsync / GetStringAsync
I suspect that further up your call stack, your code is calling Result / Wait / GetAwaiter().GetResult() on a task. If called from a UI thread, this will deadlock, as I explain on my blog.
The deadlock is caused by the async method attempting to resume on the UI context, but the UI thread is blocked waiting for the task to complete. Since the async method must complete in order to complete its task, there's a deadlock.
The proper fix is to replace that Result / Wait with await.

In your PCL use:
HttpClient httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(Url);
In case of using HTTPS.
In Android, your main activity:
protected override void OnCreate(Bundle bundle)
{
ServicePointManager.ServerCertificateValidationCallback +=(sender, cert, chain, sslPolicyErrors) => true;
}
In iOS, in your AppDelegate:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
return base.FinishedLaunching(app, options);
}

Related

Do I need a second HttpClient to make an API call to refresh JWT token inside custom DelegatingHandler?

For each API call in my App, I want to check whether the user has an expired JWT, and if so, I want to get a new one using a refresh token, and then proceed with the original request to API. This is supposed to all work in the background without the APP user experiencing any interruptions or need to login again.
I create my HttpClient like this:
static DelegatingHandler handler = new AuthenticationHandler();
static HttpClient httpClient = new HttpClient(handler)
{
BaseAddress = new Uri("https://10.0.2.2:5001/api/v1")
};
AuthenticationHandler is a custom DelegatingHandler which has an override SendAsync method. Inside that method I check if request has status Unauthorised:
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
And if it does, I need to send another request to my API with the currently owned JWT and Refresh tokens to generate new pair of tokens... Since this is an API call in the middle of another API call (as it all happens inside the custom DelegatingHandler which is a parameter for constructing my main HttpClient) - does refreshing the token needs to happen using a second HttpClient that I need to create literally to make the refresh token call?
I can't see how can I use the same HttpClient for this, how is this usually being done?
EDIT:
I can't see how I could use the same HttpClient for refreshToken call from inside AuthenticationHandler, as the handler is used to construct the HttpClient. Feels like a circular reference. I just have no idea how others do it in their code... I currently implemented it by using that second HttpClient which I only use for that one refreshToken call, and it works, but I have a feeling that there is a cleaner way to achieve this?
Btw, my (not refactored yet) SendAsync method inside AuthenticationHandler looks like this currently:
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response = new HttpResponseMessage();
request = CheckForAuthToken(request);
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
for (int i = 1; i == _maxRefreshAttempts; i++)
{
// Here I make a call to the API to refresh and return a new JWT, The authApiService uses a different HttpClient
RefreshTokenRequestModel refreshTokenRequestModel = new RefreshTokenRequestModel
{
Token = await SecureStorage.GetAsync("jwtToken"),
RefreshToken = await SecureStorage.GetAsync("refreshToken")
};
var apiResponse = await authApiService.RefreshToken(refreshTokenRequestModel);
if (apiResponse.IsSuccessStatusCode)
{
await SecureStorage.SetAsync("jwtToken", apiResponse.Content.Token);
await SecureStorage.SetAsync("refreshToken", apiResponse.Content.RefreshToken);
request = CheckForAuthToken(request);
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
}
return response;
}
catch (Exception e)
{
throw e;
}
}

App.OnResume error in Xamarin forms on Android and IOS devices

We are using xamarin forms. After an Android or IOS device resumes from background, we are making a REST call in .net that is being triggered by a timer. The first attempt on IOS returns a "The Descriptor is not a socket" error and the Android returns a "Connection refused" error. The same code works fine in Windows. Future attempts (every few seconds) in all 3 platforms work fine. Has anyone seen this and have a fix?
Code
//app on resume event
protected async override void OnResume()
{
// Handle when your app resumes
if (MainPage is RootPage)
{
RootPage mainPage = MainPage as RootPage;
if (mainPage.Detail is NavigationPage)
{
NavigationPage nvPage = mainPage.Detail as NavigationPage;
if(nvPage.CurrentPage is ThingsPage)
{
ThingsPage thPage = nvPage.CurrentPage as ThingsPage;
thPage.TurnOnTimer();
}
}
}
}
//code on the page
public void TurnOnTimer()
{
if (viewModel != null)
{
viewModel.ContinueTimer = true;
viewModel.StartAnotherTimer();
}
}
//code in view model
public async void StartAnotherTimer()
{
while (ContinueTimer)
{
try
{
DevicesUpdate devicesUpdate = await DataSource.GetDevices(LocationID, ControllerID, lastDevicesUpdateReceivedAt);
}
catch (Exception ex)
{
}
// Update the UI (because of async/await magic, this is still in the UI thread!)
if (ContinueTimer)
{
await Task.Delay(TimeSpan.FromSeconds(3));
}
}
}
public static async Task<DevicesUpdate> GetDevices(Guid locationID, Guid controllerID, DateTime lastUpdateReceivedAt)
{
DevicesUpdate devicesUpdate = await GetLastUpdatedDevices(controllerID, lastUpdateReceivedAt);
}
//code in view model
public static async Task<DevicesUpdate> GetLastUpdatedDevices(Guid controllerID,
DateTime lastUpdate)
{
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
string url = string.Format("http://appname.azurewebsites.net/api/devices?controllerid={1}&lastUpdate={2}"
, Constants.WebServerURL, controllerID, lastUpdate);
System.Net.Http.HttpResponseMessage response = await client.GetAsync(new Uri(url));
string result = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
DevicesUpdate devices = JSONHelper.Deserialize<DevicesUpdate>(result);
return devices;
}
else
{
if (response.ReasonPhrase == "UserException")
{
throw new UserException(result);
}
else
{
//throw error because the response from rest api is not a success
throw new System.Net.Http.HttpRequestException(result);
}
}
}
You might have a few things happening here that's causing problems.
GetDevices doesn't return anything. (I hope you just left out the return for brevity sake)
You are never setting ContinueTimer to false.
What iOS version are you on? In later versions, you HAVE to use HTTPS or explicitly allow non-secure connections. This shouldn't be a problem because Azure has ssl.
If you plan on running this in the background, you need to register your app as a background process.
If you don't plan on running this in the background, you might have issues with previous attempts being ran (or still trying to execute, or just have failed) and then calling more.
What is the reason for calling the 3 second timer for the network calls? What if the call takes more than 3 seconds (then you are making duplicate calls even though the first might succeed).
If you want to make your network calls more robust, check out this Blog Post by Rob Gibbons about resilient network calls.
First thing I would do is remove it from the timer because it seems like the underlying sockets are having issues cross-thread.

How do I use async and await

So here is the basic of what I am trying to do...
I have create a Web site that has web API 2 so I can do crud operations to that site.
I am trying to create the client side application in VS 2013. I have created a basic Console apps to start the project.
class PortalReposotry
{
private Uri _uri;
public PortalReposotry()
{
_uri = new Uri("http://localhost:21564/");
}
public async Task<CompanyAPI> GetCompany(string companyCode)
{
var client = new HttpClient();
client.BaseAddress = _uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(String.Format("api/companies/{0}", companyCode));
CompanyAPI content = await response.Content.ReadAsAsync<CompanyAPI>();
return await Task.Run(() => content);
}
}
class Program
{
static void Main(string[] args)
{
PortalReposotry repo = new PortalReposotry();
CompanyAPI comp = repo.GetCompany("LNCR");
Console.ReadKey();
}
}
I have no Idea why I have to use a lamda expression to return the CompanyAPI object.
All I want returned in the CompanyAPI object not the task it is running on. I am very confused on the threading and why I have to run this under task... it makes where I have to wrap everything into a task.... Which I'll be honest I have no idea how to use or decuple the actual objects from it that I want.
If you can help me out, I could be going the wrong direction all together but this is what I have found so far.
I am very confused on the threading and why I have to run this under task
You don't have to use threading here (the Task.Run is unnecessary). The Task<T> type is a "future" - it represents an asynchronous operation that will have a result value of type T in the future. That's why you need to use tasks with asynchronous code (technically, you could use callbacks instead, but that's much more painful - tasks are easier).
I have created a basic Console apps to start the project.
Asynchronous console apps are a bit weird. You need to block the main thread so the application doesn't exit. This is unnecessary in a real UI client-side app (which I assume your sample project will eventually become). So for now you can just do a hack like this:
static void Main(string[] args)
{
MainAsync().Wait();
}
static async Task MainAsync()
{
PortalReposotry repo = new PortalReposotry();
CompanyAPI comp = await repo.GetCompanyAsync("LNCR");
Console.ReadKey();
}
The call to Wait() should only be done inside a Console app's Main method. You shouldn't ever call it in a UI application.
Since Task.Run isn't needed, you can clean up that method, too:
public async Task<CompanyAPI> GetCompanyAsync(string companyCode)
{
var client = new HttpClient();
client.BaseAddress = _uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(String.Format("api/companies/{0}", companyCode));
return await response.Content.ReadAsAsync<CompanyAPI>();
}
in your GetCompany method, the await and Task.Run is unnecessary, you can just return the content.
public async Task<CompanyAPI> GetCompany(string companyCode)
{
var client = new HttpClient();
client.BaseAddress = _uri;
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(String.Format("api/companies/{0}", companyCode));
CompanyAPI content = await response.Content.ReadAsAsync<CompanyAPI>();
return content
}
then in your main method you would just call it like so. Disclaimer,
this will make it run synchronously. You can use the ContinueWith method of the Task object to add a "callback" to the task. https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.continuewith(v=vs.110).aspx
CompanyAPI comp = repo.GetCompany("LNCR").Result;

WP7 Get return value from Async HttpWebRequest

I have wrote this code:
private void button1_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest request = SendRequests.CreateRequest(serverTextBox.Text);
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ReadWebRequestCallback), request);
}
private void ReadWebRequestCallback(IAsyncResult callbackResult)
{
HttpWebRequest myRequest = (HttpWebRequest)callbackResult.AsyncState;
try
{
HttpWebResponse myResponse = (HttpWebResponse)myRequest.EndGetResponse(callbackResult);
using (StreamReader httpwebStreamReader = new StreamReader(myResponse.GetResponseStream()))
{
string results = httpwebStreamReader.ReadToEnd();
loginValue = Parser.ParseLoginValue(results);
}
myResponse.Close();
}
catch (WebException we)
{
//
}
}
My problem at this point is that after the Click on the button I need the return value (here loginValue) of the BeginGetResponse to go on with the execution of the application.
I know that this is against the entire sense of asynchronous calls, but, there's a way to wait for the results before going on with the main thread?
Unfortunately, you have answered your own question. On Windows Phone, there is no way to do a synchronous web call.
But you don't really need to. If there is really nothing for your user to do while waiting for the response from the web, slap up a translucent overlay with a progress bar (or even better, use the global progress bar in the system try) and abide.
By contrast, if you were waiting for a synchronous call from the internet to return (which on a mobile device could take a long time), the UI would be locked and the user would think the application had hung (which, technically, is true).
Asynchronicity is your friend. Play nice with it.

SmtpClient.SendAsync blocking my ASP.NET MVC Request

I have a Action that sends a simple email:
[HttpPost, ActionName("Index")]
public ActionResult IndexPost(ContactForm contactForm)
{
if (ModelState.IsValid)
{
new EmailService().SendAsync(contactForm.Email, contactForm.Name, contactForm.Subject, contactForm.Body, true);
return RedirectToAction(MVC.Contact.Success());
}
return View(contactForm);
}
And a email service:
public void SendAsync(string fromEmail, string fromName, string subject, string body, bool isBodyHtml)
{
MailMessage mailMessage....
....
SmtpClient client = new SmtpClient(settingRepository.SmtpAddress, settingRepository.SmtpPort);
client.EnableSsl = settingRepository.SmtpSsl;
client.Credentials = new NetworkCredential(settingRepository.SmtpUserName, settingRepository.SmtpPassword);
client.SendCompleted += client_SendCompleted;
client.SendAsync(mailMessage, Tuple.Create(client, mailMessage));
}
private void client_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
Tuple<SmtpClient, MailMessage> data = (Tuple<SmtpClient, MailMessage>)e.UserState;
data.Item1.Dispose();
data.Item2.Dispose();
if (e.Error != null)
{
}
}
When I send a email, I am using Async method, then my method SendAsync return immediately, then RedirectToAction is called. But the response(in this case a redirect) isnĀ“t sent by ASP.NET until client_SendCompleted is completed.
Here's what I'm trying to understand:
When watching the execution in Visual Studio debugger, the SendAsync returns immediately (and RedirectToAction is called), but nothing happens in the browser until email is sent?
If i put a breakpoint inside client_SendCompleted, the client stay at loading.... until I hit F5 at debugger.
This is by design. ASP.NET will automatically wait for any outstanding async work to finish before finishing the request if the async work was kicked off in a way that calls into the underlying SynchronizationContext. This is to ensure that if your async operation tries to interact with the HttpContext, HttpResponse, etc. it will still be around.
If you want to do true fire & forget, you need to wrap your call in ThreadPool.QueueUserWorkItem. This will force it to run on a new thread pool thread without going through the SynchronizationContext, so the request will then happily return.
Note however, that if for any reason the app domain were to go down while your send was still in progress (e.g. if you changed the web.config file, dropped a new file into bin, the app pool recycled, etc.) your async send would be abruptly interrupted. If you care about that, take a look at Phil Haacks WebBackgrounder for ASP.NET, which let's you queue and run background work (like sending an email) in such a way that will ensure it gracefully finishes in the case the app domain shuts down.
This is an interesting one. I've reproduced the unexpected behaviour, but I can't explain it. I'll keep digging.
Anyway the solution seems to be to queue a background thread, which kind of defeats the purpose in using SendAsync. You end up with this:
MailMessage mailMessage = new MailMessage(...);
SmtpClient client = new SmtpClient(...);
client.SendCompleted += (s, e) =>
{
client.Dispose();
mailMessage.Dispose();
};
ThreadPool.QueueUserWorkItem(o =>
client.SendAsync(mailMessage, Tuple.Create(client, mailMessage)));
Which may as well become:
ThreadPool.QueueUserWorkItem(o => {
using (SmtpClient client = new SmtpClient(...))
{
using (MailMessage mailMessage = new MailMessage(...))
{
client.Send(mailMessage, Tuple.Create(client, mailMessage));
}
}
});
With .Net 4.5.2, you can do this with ActionMailer.Net:
var mailer = new MailController();
var msg = mailer.SomeMailAction(recipient);
var tcs = new TaskCompletionSource<MailMessage>();
mailer.OnMailSentCallback = tcs.SetResult;
HostingEnvironment.QueueBackgroundWorkItem(async ct =>
{
msg.DeliverAsync();
await tcs.Task;
Trace.TraceInformation("Mail sent to " + recipient);
});
Please read this first: http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
I sent the bug to Microsoft Connect https://connect.microsoft.com/VisualStudio/feedback/details/688210/smtpclient-sendasync-blocking-my-asp-net-mvc-request

Resources