I just started learning C#, and I have also never experienced in developing mobile environments. I have experience in Java and I could adopt some knowledge from there.
I have VisualStudio 2017RC, and started a Xamarin.Android project. Furthermore there is a ASP.NET site for registration and login, and token generation (SQLite user storage). My goal is to make a view in xamarin where the user can type his/her name and password then can login to the website where he/she get an access token.
I have already made a LoginView, it was a piece of cake... Now I have to post the filled data to the website and log in to the page using c# in Xamarin.Android. Lets see how far I have got to.
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.LoginWebview);
//code for getting the login details(emial, password) from the user
//code for getting the access token
//apiBaseUri is fixed value
MainAsync(email, password, apiBaseUri, loginToken).Wait();
}
public static async Task MainAsync(string email, string password, string apiBaseUri, string loginToken)
{
var token = await GetApiTokenAsync(email, password, apiBaseUri, loginToken);
}
public static async Task<string> GetApiTokenAsync(string email, string password, string apiBaseUri, string tokenValue)
{
var token = string.Empty;
var handler = new HttpClientHandler()
{
AllowAutoRedirect = false
};
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri(apiBaseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.Timeout = TimeSpan.FromSeconds(60);
//set cookie
client.DefaultRequestHeaders.Add("Set-Cookie", CookieManager.Instance.GetCookie(apiBaseUri));
//setup login data
var formContent = new FormUrlEncodedContent(new[]
{
//new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("Email", username),
new KeyValuePair<string, string>("Password", password),
new KeyValuePair<string,string>("__RequestVerificationToken",tokenValue),
new KeyValuePair<string,string>("RememberMe","false")
});
//send request
HttpResponseMessage responseMessage = await client.PostAsync("/Login?returnurl=%2FGame%2Fkz%2FToken", formContent);
var responseJson = await responseMessage.Content.ReadAsStringAsync();
var jObject = JObject.Parse(responseJson);
token = jObject.GetValue("access_token").ToString();
return token;
}
}
}
Well at line
HttpResponseMessage responseMessage = await client.PostAsync("/Login?returnurl=%2FGame%2Fkz%2FToken", formContent);
The program hangs and I got no access token back.
Let's see the issue from the website point of view: in the webbrowser the login form can be opened with the following link: (I will use some dummy value ("game") in the url in order to keep it private) http://www.game.nl/PlayGame/kz/Account/Login?ReturnUrl=%2FGame%2Fkz%2Ftoken
With opening this website I can type the email and the password. Then If I click on the log in button I got an access token (which will be used for later purposes). I would like to get to this point, but of course in c# with android phone
I am going to list some additional info about the website because I think they are also relevant to the solution. The details are the following, if I log in with the website. (I check the details with Google Chrome developer tool, at network tab)
General:
FormData:
The source of the form data:
Email=example%40company.nl&Password=chocolate09%23&__RequestVerificationToken=CfDJ8L1aKkmfKmBLlQIwHTacxWKOtRekEcOfxKQGBrXUEkE2XpWyxYmMKOTlgZ7gCZq3FEx8ILF_VgQcsjBztZhCwUUYSV966s1BgjFs5I8IefGSoei5hfzK-WQQr8Ztjpn3oGq40neXx4_iBUUMRjFpKz_DdBqzQ&RememberMe=false
So I am stuck at this point, and I do not know why it hangs at
await client.PostAsync("/Login?returnurl=%2FGame%2Fkz%2FToken", formContent);
The last lines of the VS console output:
02-11 00:07:01.404 D/Mono ( 2853): [0x982bf930] hill climbing, change max number of threads 2
02-11 00:07:07.829 D/Mono ( 2853): [0x9847f930] worker finishing
Thread finished: <Thread Pool> #4
The thread 'Unknown' (0x4) has exited with code 0 (0x0).
Your problem I believe is this line in OnCreate
:
MainAsync(email, password, apiBaseUri, loginToken).Wait();
Specifically the .Wait()
statement. OnCreate
is always called on the UI thread. Whenever you use async/await and wait for the result using .Wait()
or .Result
bad things happen and your app will hang as the continuation cannot jump back to the UI thread - and you will see this in the call to PostAsync
as this is the first call that will actually use a different thread to run on.
I guess the reason you are calling .Wait()
is because OnCreate
is not an async method - well the good news is it can be. I would mark it as async
and await
the login (you can mark void methods as async, there are pitfalls with doing so, so be sure to catch all exceptions inside the method!)
// Mark the method as async
protected override async void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.LoginWebview);
// remove the .Wait() and await the method call
await MainAsync(email, password, apiBaseUri, loginToken);
}