Xamarin Forms http async request - c#

i am trying to make an asynchronous call to a webservice.
I would like to make this call when opening the app (App.xaml.cs).
According to the answer that comes back to me, it has to navigate to a particular page
But I do not work.
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
try
{
CheckLogin().Wait();
}
catch (Exception e)
{
var t = e;
}
}
private static async Task CheckLogin()
{
try
{
var login = new Login
{
Email = "test#test.com",
Password = "test",
};
var client = new HttpClient { BaseAddress = new Uri("http://www.api.com/test/") };
var data = JsonConvert.SerializeObject(login);
var content = new StringContent(data, Encoding.UTF8, "application/json");
var response = await client.PostAsync(#"api/it-IT/auth/token", content); //crash without error, freeze
if (response.IsSuccessStatusCode)
{
var successResult = JsonConvert.DeserializeObject<HttpResponseMessage>(response.Content.ReadAsStringAsync().Result);
if (successResult != null)
{
//return true;
}
else
{
//return false;
}
}
}
catch (Exception e)
{
var t = e;
}
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<NavigationPage>();
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<MainPage2>();
Container.RegisterTypeForNavigation<MainPage3>();
}
}
When does the postasync call does not go more forward, not I get no errors, but does not proceed.
But if I try the same code in an application console, everything works fine, why?
class Program
{
static void Main(string[] args)
{
Console.WriteLine("A");
CheckLogin().Wait();
Console.WriteLine("K");
Console.ReadKey();
}
private static async Task CheckLogin()
{
try
{
var login = new Login
{
Email = "test#test.com",
Password = "#test",
};
var client = new HttpClient { BaseAddress = new Uri("http://www.api.com/test/") };
var data = JsonConvert.SerializeObject(login);
var content = new StringContent(data, Encoding.UTF8, "application/json");
var response = await client.PostAsync(#"api/it-IT/auth/token", content);
if (response.IsSuccessStatusCode)
{
}
}
catch (Exception e)
{
var t = e;
}
}
}
If I try to do the same operation within a command with wait I do not work the same error, but if I do await, it will work fine, but in App.xaml.cs in OnInitialized() I can not put await
public DelegateCommand callCommand { get; set; }
public MainPage2ViewModel()
{
callCommand = new DelegateCommand(Call);
}
private void Call()
{
//await CheckLogin(); // work
CheckLogin().Wait(); // not work the same problem
var i = "pippo";
}
private async Task CheckLogin()
{
....
}
Is there anything to set with xamarin or with prism?

I've also the same strange error...
i fix with this workaround (use an async void that wrap async task)...
public App()
{
InitializeComponent();
Current.MainPage = new LoadingPage();
}
protected override void OnStart()
{
MagicInit();
base.OnStart();
}
public static async void MagicInit()
{
var f = await FileSystem.Current.LocalStorage.CreateFileAsync("db.sqlite", CreationCollisionOption.OpenIfExists);
DbConnection = f.Path;
await DataService.DbFill();
User = await DataService.Instance.Table<SpUser>().FirstOrDefaultAsync();
Current.MainPage = User != null ? (Page)new MainPage() : new LoginPage();
}

Related

Authenticate an EWS application by using OAuth within custom class

I have code to authenticate with EWS using oAuth working fine if I call it from winform button click, but not working if I place my code inside custom class and call it inside constructor I don't know what is the problem ?
Authenticate Code function :
public async Task<AuthenticationResult> oAuthLoginRequest()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var cca = ConfidentialClientApplicationBuilder
.Create(Settings.Default.appId)
.WithClientSecret(Settings.Default.clientSecret)
.WithTenantId(Settings.Default.tenantId)
.Build();
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
try
{
_authenticationResult = await cca.AcquireTokenForClient(ewsScopes)
.ExecuteAsync();
return _authenticationResult;
}
catch (Exception ex)
{
string.Format("oAuthLoginRequest: Exception= {0}", ex.Message).LogIt(TLogType.ltError);
return _authenticationResult;
}
}
Working well and I got access token :
private async void button1_Click(object sender, EventArgs e)
{
oAuthLoginRequest();
//Access Token Available here
var accessToken = _authenticationResult.AccessToken ; //Working fine
}
NOT WORKING :
public class TServiceController
{
private bool _started = false;
public bool Started { get { return _started; } }
TEWSService mailService = null;
public ExchangeService _service = null;
public AuthenticationResult _authenticationResult = null;
public DateTimeOffset TokenExpiresOn { get; set; }
public async Task<AuthenticationResult> oAuthLoginRequest()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var cca = ConfidentialClientApplicationBuilder
.Create(Settings.Default.appId)
.WithClientSecret(Settings.Default.clientSecret)
.WithTenantId(Settings.Default.tenantId)
.Build();
// "https://outlook.office365.com/.default" ,"https://outlook.office365.com/EWS.AccessAsUser.All" , "https://graph.microsoft.com/Mail.Send"
// "https://ps.outlook.com/full_access_as_app"
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
try
{
_authenticationResult = await cca.AcquireTokenForClient(ewsScopes).ExecuteAsync();
TokenExpiresOn = _authenticationResult.ExpiresOn;
("AccessToken:" + _authenticationResult.AccessToken).LogIt(TLogType.ltDebug);
}
catch (Exception ex)
{
string.Format("oAuthLoginRequest: Exception= {0}", ex.Message).LogIt(TLogType.ltError);
}
return _authenticationResult;
}
public TServiceController()
{
var auth = oAuthLoginRequest().Result; //STUCK HERE
"Service controller started.".LogIt();
} //end constructore
} //END CLASS
Any explanation ?
I tried two methods one of them work just fine in winform click button and other solution not working within my class constructor .

Picker List in Xamarin Forms

I'm creating an app that should import a lis from a Rest Api that connects with an sql server
it is working perfect in postman
but in Xamarin Evrithing comes blank
here is my code
this is the repo
public Alamacenes[] getAlmacenes()
{
try
{
Alamacenes[] almacenes;
var URLWebAPI = "https://www.avila.somee.com/ApiAlexa/api/Almacenes";
using (var Client = new System.Net.Http.HttpClient())
{
var JSON = Client.GetStringAsync(URLWebAPI);
almacenes = JsonConvert.DeserializeObject<Alamacenes[]>(JSON.Result);
}
return almacenes;
}
catch (Exception ex)
{
throw ex;
}
}
this is my codebehind
protected async void OnAppearing()
{
Repositorio repo = new Repositorio();
Alamacenes[] listalmacenes = repo.getAlmacenes();
foreach (var item in listalmacenes)
{
base.OnAppearing();
Almacen.Items.Add(item.CodigoAlmacen.ToString());
Almacen.Items.IndexOf(item.Almacen.ToString());
}
}
and this image is the result
enter image description here
i would like to know if i'm doing something wrong with my code
I think you are setting the list to the picker while is empty, You should await the response and after that set the item source to the picker. Take a look on this quick sample.
//XAML Code
<Picker x:Name="almacenPicker" ItemDisplayBinding="{Binding Almacen}" />
//Code behind code
protected override async void OnAppearing()
{
base.OnAppearing();
var almacenList = await GetAlamacenes();
almacenPicker.ItemsSource = almacenList;
}
public Task<List<Almacene>> GetAlamacenes()
{
return GetAsync<List<Almacene>>("https://www.avila.somee.com/ApiAlexa/api/Almacenes");
}
public async Task<TResult> GetAsync<TResult>(string uri)
{
var httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync(uri);
var content = await response.Content.ReadAsStringAsync();
return await Task.Run(() => JsonConvert.DeserializeObject<TResult>(content));
}

Close .Auth/login/done after MSAL login successful in Xamarin application

I have been setting up AD(MSAL) Authentication using azure with my application but am having an issue closing the window that appears after successful sign in. The page that appears within the embedded browser with the link returning to my API homepage simply states "You have successfully signed in" with a link below to return to previous page with goes to my API home page.
The below is my code in my App.xaml.cs
public partial class App : Application
{
public static IPublicClientApplication PCA = null;
public static string ClientID = "********-****-****-****-**********";
public static string[] Scopes = { "User.Read" };
public static string Username = string.Empty;
public static object ParentWindow { get; set; }
public App()
{
InitializeComponent();
}
protected override async void OnStart()
{
PCA = PublicClientApplicationBuilder.Create(ClientID)
//.WithRedirectUri($"msal{ClientID}://auth")
.WithRedirectUri("https://kpiapp-api-dev.azurewebsites.net/.auth/login/aad/callback")
.WithIosKeychainSecurityGroup("com.microsoft.adalcache")
.WithAuthority(AzureCloudInstance.AzurePublic, "********-****-****-****-**********") //TenantID
.Build();
MainPage = new NavigationPage(new LoginPage());
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
And my Loginpage.xaml.cs:
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
}
async void OnSignIn(object sender, EventArgs e)
{
AuthenticationResult authResult = null;
IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();
var current = Connectivity.NetworkAccess;
bool connectionFound = false;
if (current == NetworkAccess.Internet)
{
connectionFound = true;
}
string APIData = "";
if(connectionFound == true)
{
try
{
if (SignInButton.Text == "Sign in")
{
try
{
IAccount firstAccount = accounts.FirstOrDefault();
authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
.ExecuteAsync();
}
catch (MsalUiRequiredException ex)
{
try
{
authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
.WithParentActivityOrWindow(App.ParentWindow)
.ExecuteAsync();
}
catch (Exception ex2)
{
await DisplayAlert("Acquire token interactive failed. See exception message for details: ", ex2.Message, "Dismiss");
}
}
if (authResult != null)
{
var content = await GetHttpContentWithTokenAsync(authResult.AccessToken);
SignInButton.Text = "Sign out";
}
}
else
{
while (accounts.Any())
{
await App.PCA.RemoveAsync(accounts.FirstOrDefault());
accounts = await App.PCA.GetAccountsAsync();
}
});
SignInButton.Text = "Sign in";
}
}
catch (Exception ex)
{
await DisplayAlert("Authentication failed. See exception message for details: ", ex.Message, "Dismiss");
}
await Task.Yield();
APIData = getAPIData();
}
else
{
await DisplayAlert("Connection Error", "Check your internet connection and try again", "Try again");
}
if (APIData != "ConnectionError")
{
await Navigation.PushAsync(new MainPage(APIData));
}
else
{
await Task.Delay(500);
await DisplayAlert("API Download error", "Error connecting to API", "Try again");
}
//MainPage = new MainPage(APIData);
}
public async Task<string> GetHttpContentWithTokenAsync(string token)
{
try
{
//get data from API
HttpClient client = new HttpClient();
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me");
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await client.SendAsync(message);
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
catch (Exception ex)
{
await DisplayAlert("API call to graph failed: ", ex.Message, "Dismiss");
return ex.ToString();
}
}
private string getAPIData()
{
string APIData = "";
try
{
APIData = new WebClient().DownloadString("****/api/data");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
APIData = "ConnectionError";
}
return APIData;
}
}
I'm aware this is doing nothing with that sign in and won't access the api data at the moment. I'm really just looking to get the authentication window closed and then work from there.
Thanks
I managed to solve this issue, by adding .WithUseEmbeddedWebView(true) to the second call of authResult so it appears like this:
catch (MsalUiRequiredException ex)
{
try
{
authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
.WithParentActivityOrWindow(App.ParentWindow)
.WithUseEmbeddedWebView(true)
.ExecuteAsync();
}
catch (Exception ex2)
{
await DisplayAlert("Acquire token interactive failed. See exception message for details: ", ex2.Message, "Dismiss");
}
}

Exception thrown: 'System.TypeLoadException' in Unknown Module in UWP Background Task

This code gives me the exception
Exception thrown: 'System.TypeLoadException' in Unknown Module
public sealed class SampleBackgroundTask2 : IBackgroundTask
{
EasClientDeviceInformation currentDeviceInfo;
BackgroundTaskCancellationReason _cancelReason = BackgroundTaskCancellationReason.Abort;
BackgroundTaskDeferral _deferral = null;
IBackgroundTaskInstance _taskInstance = null;
ThreadPoolTimer _periodicTimer = null;
//
// The Run method is the entry point of a background task.
//
public void Run(IBackgroundTaskInstance taskInstance)
{
currentDeviceInfo = new EasClientDeviceInformation();
var cost = BackgroundWorkCost.CurrentBackgroundWorkCost;
var settings = ApplicationData.Current.LocalSettings;
settings.Values["BackgroundWorkCost2"] = cost.ToString();
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
_deferral = taskInstance.GetDeferral();
_taskInstance = taskInstance;
_periodicTimer = ThreadPoolTimer.CreateTimer(new TimerElapsedHandler(PeriodicTimerCallbackAsync), TimeSpan.FromSeconds(1));
}
private async void PeriodicTimerCallbackAsync(ThreadPoolTimer timer)
{
try
{
var httpClient = new HttpClient(new HttpClientHandler());
string urlPath = (string)ApplicationData.Current.LocalSettings.Values["ServerIPAddress"] + "/Api/Version1/IsUpdatePersonal";
HttpResponseMessage response = await httpClient.PostAsync(urlPath,
new StringContent(JsonConvert.SerializeObject(currentDeviceInfo.Id.ToString()), Encoding.UTF8, "application/json")); // new FormUrlEncodedContent(values)
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
string jsonText = await response.Content.ReadAsStringAsync();
var customObj = JsonConvert.DeserializeObject<bool>(jsonText, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
if (customObj) // Если TRUE то да надо сообщить пользователю о необходимости обновления
{
ShowToastNotification("Ttitle", "Message");
}
}
}
catch (HttpRequestException ex)
{
}
catch (Exception ex)
{
}
finally
{
_periodicTimer.Cancel();
_deferral.Complete();
}
}
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
_cancelReason = reason;
}
}
If I comment async/await and HttpClient places then there is no exception.
So what's wrong with my code?
Or Is it done well to use UWP Background Task to make async GET/POST?
I have tried some classic solution like
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral _deferral = taskInstance.GetDeferral();
//
// Start one (or more) async
// Use the await keyword
//
// await SomeMethodAsync();
var uri = new System.Uri("http://www.bing.com");
using (var httpClient = new Windows.Web.Http.HttpClient())
{
// Always catch network exceptions for async methods
try
{
string result = await httpClient.GetStringAsync(uri);
}
catch (Exception ex)
{
// Details in ex.Message and ex.HResult.
}
}
_deferral.Complete();
}
but once I put HttpClient inside of SomeMethodAsync() it does not work with the error above.
This solution does not help HttpClient.GetAsync fails in background task with lock screen access and both TimeTrigger or MaintenanceTrigger
Thanks!
I simplified the solution a bit and removed the ThreadPoolTimer since I was not sure why it was being used from the code. Please mention if it is required for the solution.
If the ThreadPoolTimer is optional then you can try the following code :
public sealed class SampleBackgroundTask2 : IBackgroundTask
{
EasClientDeviceInformation currentDeviceInfo;
BackgroundTaskCancellationReason _cancelReason = BackgroundTaskCancellationReason.Abort;
BackgroundTaskDeferral _deferral = null;
//
// The Run method is the entry point of a background task.
//
public async void Run(IBackgroundTaskInstance taskInstance)
{
currentDeviceInfo = new EasClientDeviceInformation();
var cost = BackgroundWorkCost.CurrentBackgroundWorkCost;
var settings = ApplicationData.Current.LocalSettings;
settings.Values["BackgroundWorkCost2"] = cost.ToString();
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
_deferral = taskInstance.GetDeferral();
await asynchronousAPICall();
_deferral.Complete(); //calling this only when the API call is complete and the toast notification is shown
}
private async Task asynchronousAPICall()
{
try
{
var httpClient = new HttpClient(new HttpClientHandler());
string urlPath = (string)ApplicationData.Current.LocalSettings.Values["ServerIPAddress"] + "/Api/Version1/IsUpdatePersonal";
HttpResponseMessage response = await httpClient.PostAsync(urlPath,
new StringContent(JsonConvert.SerializeObject(currentDeviceInfo.Id.ToString()), Encoding.UTF8, "application/json")); // new FormUrlEncodedContent(values)
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
string jsonText = await response.Content.ReadAsStringAsync();
var customObj = JsonConvert.DeserializeObject<bool>(jsonText, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
if (customObj) // Если TRUE то да надо сообщить пользователю о необходимости обновления
{
ShowToastNotification("Ttitle", "Message");
}
}
}
catch (HttpRequestException ex)
{
}
catch (Exception ex)
{
}
finally
{
_deferral.Complete();
}
}
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
_cancelReason = reason;
}
}

checking Internet connection with HttpClient

I am having difficulties to understand on how the bellow code could handle occasional internet connection loss. Ideally I would like to pause the app, once the connection is lost, and resume when it is up again. Is there any guideline on how to do it?
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
HttpClient client = new HttpClient(clientHandler) { MaxResponseContentBufferSize = 1000000 };
HttpResponseMessage response = await client.GetAsync(Url, ct);
The following example is not a direct solution, but it is an example I built to show how to return "pre-canned" content to requests whilst offline and then return back online when connectivity is restored. If you can get what I'm doing here, building what you want should be fairly easy.
[Fact]
public async Task Getting_a_response_when_offline()
{
var offlineHandler = new OfflineHandler(new HttpClientHandler(), new Uri("http://oak:1001/status"));
offlineHandler.AddOfflineResponse(new Uri("http://oak:1001/ServerNotRunning"),
new HttpResponseMessage(HttpStatusCode.NonAuthoritativeInformation)
{
Content = new StringContent("Here's an old copy of the information while we are offline.")
});
var httpClient = new HttpClient(offlineHandler);
var retry = true;
while (retry)
{
var response = await httpClient.GetAsync(new Uri("http://oak:1001/ServerNotRunning"));
if (response.StatusCode == HttpStatusCode.OK) retry = false;
Thread.Sleep(10000);
}
}
public class OfflineHandler : DelegatingHandler
{
private readonly Uri _statusMonitorUri;
private readonly Dictionary<Uri, HttpResponseMessage> _offlineResponses = new Dictionary<Uri, HttpResponseMessage>();
private bool _isOffline = false;
private Timer _timer;
public OfflineHandler(HttpMessageHandler innerHandler, Uri statusMonitorUri)
{
_statusMonitorUri = statusMonitorUri;
InnerHandler = innerHandler;
}
public void AddOfflineResponse(Uri uri, HttpResponseMessage response)
{
_offlineResponses.Add(uri,response);
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (_isOffline == true) return OfflineResponse(request);
try
{
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.ServiceUnavailable || response.StatusCode == HttpStatusCode.BadGateway)
{
MonitorOfflineState();
return OfflineResponse(request);
}
return response;
}
catch (WebException ex)
{
MonitorOfflineState();
return OfflineResponse(request);
}
}
private void MonitorOfflineState()
{
_isOffline = true;
_timer = new Timer( async state =>
{
var request = new HttpRequestMessage() {RequestUri = _statusMonitorUri};
try
{
var response = await base.SendAsync(request, new CancellationToken());
if (response.StatusCode == HttpStatusCode.OK)
{
_isOffline = false;
_timer.Dispose();
}
}
catch
{
}
}, null, new TimeSpan(0,0,0),new TimeSpan(0,1,0));
}
private HttpResponseMessage OfflineResponse(HttpRequestMessage request)
{
if (_offlineResponses.ContainsKey(request.RequestUri))
{
return _offlineResponses[request.RequestUri];
}
return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable);
}
}
}

Categories

Resources