I am trying to implement Google SSO in my C# web application. It seemed to be pretty straightforward. Based on this tutorial, Google does its magic in the web browser
Although brand new to this async/await paradigm, I do have some experience in AngularJS / NodeJS promises / callbacks.
But not use Typescript, right?
My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack.
They should. Bad things can happen if you don't.
I think it means ending the service call before the async response finishes and the service can act on the result.
No! The compiler generates a state machine for methods with the async
modifier and the await
keyword means "go do something else and I'll come back here when I'm done".
Also, for creating unit tests, putting async into the [TestMethod] method makes it completely disappear from the test explorer. I'm not sure how to test/debug this conundrum.
You're probably making your test methods async void
. They should be async Task
in order for the test engine to know when the test is done.
Have a look at Stephen Cleary's blog. He has lots of content on async-await
.
Paulo, Thank you!!
I was able to get this working with your advice on the last part about the test method. I had to change this:
//THIS TEST METHOD DOESN'T SHOW IN THE TEST EXPLORER
[TestMethod]
public async void AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
}
To this:
//THIS TEST METHOD SHOWS IN THE TEST EXPLORER
[TestMethod]
public async Task AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
}
NOW -- as an additional gotcha that was hanging me up, this will also cause a unit test to disappear from the test explorer, which I found out through lazy copy/pasting a non-test method's definition and mindlessly just adding a return when the build output told me I needed to return a value.
//THIS TEST METHOD DOESN'T SHOW IN THE TEST EXPLORER DUE TO RETURN VALUE
[TestMethod]
public async Task<bool> AuthenticateGoogle()
{
string lToken = "[JWT TOKEN HERE]";
wfUser lUser = new wfUser(_wfContext);
var lAuthenticateResult = await lUser.wfAuthenticateGoogle(lToken);
Assert.IsTrue(lAuthenticateResult, "JWT Token Validated");
return true;
}
In addition to the excellent blog you shared, this article from MSDN Magazine entitled Async Programming : Unit Testing Asynchronous Code helped me get my brain around it too.
What was hanging me up with all of this was mixing synchronous and async methods which, to your point, was not working well. The code seemed to skip the debug points I had set after the await calls as if it never ran them or, if it did run them, ran them somewhere I could not see and couldn't log to the debugger.
It was one of those terrible moments late on a Friday night where a developer starts to question both competence and sanity! :)
Thanks again for the help!
p.s. I incorrectly typed Angular JS in my question and meant Angular 4...and I am using TypeScript there. Some day I'll stop incorrectly referring the newer versions of Angular as AngularJS.
My challenge is that it seems async methods can only be called by other async methods all the way back up the call-stack.
It's not entirely true. You can use async method in sync methods. Of course you are losing most of the 'async' effect, but sometimes you have to. As async methods returs tasks you have some options.
When Task is returning result, any reference to t.Result
will block execution till it's known. Another option is to use Task.Wait(preferably with timeout). For example code to validate google jwt token:
public bool ValidateGoogleToken(string token)
{
try
{
if(GoogleJsonWebSignature.ValidateAsync(token).Wait(1000))
return true; //success
//timeout exceeded
}
catch (Exception e)
{
//tampered token
}
return false;
}