On Basic Authentication of node express, I need to pass an extra parameter to node express callback, that parameter is role. That role parameter is akin to Authorize attribute of ASP.NET MVC.
Here's a rough signature of node express callback:
interface Func { (random: number, basicAuthUser: string) : boolean; } function doSomething(...actions: Func[]) : void { var basicAuthUser = "davegrohl"; // obtained from browser's Basic Authentication for(var i = 0; i < actions.length; ++i) { var a = actions[i]; var okToContinue = a(Math.random() * 100, basicAuthUser); if (!okToContinue) break; } } console.log(''); console.log('without authorization'); doSomething( (i,u) => { console.log('Alpha ' + i); return true; }, (i,u) => { console.log('Beta ' + i); return true; } );
Then I think, authentication and authorization role mechanism could be slotted on the first parameter of the array. But how would I pass an extra parameter on the callback? Then an enlightenment from C# came. On C# object, the properties can be accessed even if the method reference is passed to callback. The solution could be written in C# like the following:
Live Code: https://dotnetfiddle.net/tSenYK
using System; using System.Linq; public class Program { public static void Main() { Console.WriteLine("Without authorization:"); DoSomething( (i,u) => { Console.WriteLine("Alpha " + i); return true; }, (i,u) => { Console.WriteLine("Beta " + i); return true; } ); Console.WriteLine(); Console.WriteLine("Rockstar activity:"); DoSomething( new Authorizer("rockstar").ActionToDo, // just pass the reference of method (i,u) => { Console.WriteLine("Alpha " + i); return true; }, (i,u) => { Console.WriteLine("Beta " + i); return true; } ); Console.WriteLine(); Console.WriteLine("Guest activity:"); DoSomething( new Authorizer("guest").ActionToDo, (i,u) => { Console.WriteLine("Alpha " + i); return true; }, (i,u) => { Console.WriteLine("Beta " + i); return true; } ); } // third-party framework. cannot change the signature public static void DoSomething(params Func<int, string, bool>[] actions) { var basicAuthUser = "davegrohl"; // obtained from browser's Basic Authentication for(int i = 0; i < actions.Length; ++i) { var a = actions[i]; bool okToContinue = a(new Random().Next(10), basicAuthUser); if (!okToContinue) break; } } } class Authorizer { public string RoleAllowed { get; set; } public Authorizer(string roleAllowed) { this.RoleAllowed = roleAllowed; } string GetRole(string basicAuthUser) { return "rockstar"; // davegrohl's role fetched from database } public bool ActionToDo(int i, string basicAuthUser) { // Database operation here. // Get the role of basicAuthUser var role = this.GetRole(basicAuthUser); return role == this.RoleAllowed; } }
Output: Without authorization: Alpha 4 Beta 4 Rockstar activity: Alpha 4 Beta 4 Guest activity:
So I just need write the equivalent in TypeScript/JavaScript:
Live Code at TypeScriptLang
class Authorizer { roleAllowed : string; constructor(roleAllowed: string) { this.roleAllowed = roleAllowed; } private getRole(basicAuthUser: string) : string { return "rockstar"; // davegrohl's role fetched from database } actionToDo(i: number, basicAuthUser: string) : boolean { // Database operation here. // Get the role of basicAuthUser var role = this.getRole(basicAuthUser); // davegrohl's role fetched from database return role === this.roleAllowed; } } interface Func { (random: number, basicAuthUser: string) : boolean; } function doSomething(...actions: Func[]) : void { var basicAuthUser = "davegrohl"; // obtained from browser's Basic Authentication for(var i = 0; i < actions.length; ++i) { var a = actions[i]; var okToContinue = a(Math.random() * 100, basicAuthUser); if (!okToContinue) break; } } console.log(''); console.log('without authorization'); doSomething( (i,u) => { console.log('Alpha ' + i); return true; }, (i,u) => { console.log('Beta ' + i); return true; } ); console.log(''); console.log("Rockstar activity:"); doSomething( new Authorizer("rockstar").actionToDo, (i,u) => { console.log("Alpha " + i); return true; }, (i,u) => { console.log("Beta " + i); return true; } ); console.log(''); console.log("Guest activity:"); doSomething( new Authorizer("guest").actionToDo, (i,u) => { console.log("Alpha " + i); return true; }, (i,u) => { console.log("Beta " + i); return true; } );
However, it didn't work. The this object looks like it's not an instance af Authorizer class. The error says:

Then I add a console.log of the this object.
actionToDo(i: number, basicAuthUser: string) : boolean { // Database operation here. // Get the role of basicAuthUser console.log(this); var role = this.getRole(basicAuthUser); // davegrohl's role fetched from database return role === this.roleAllowed; }
Here's the output, oops it looks like the this object is not carried when passing the reference of the method to callbacks.

To make the long story short, the this object is not included when accessing the reference of the method. Not similar to C#
The solution is to bind the this object before passing the reference of the method to callback.
Correct:
Live Code at TypeScriptLang
class Authorizer { roleAllowed : string; constructor(roleAllowed: string) { this.roleAllowed = roleAllowed; } getBindedActionToDo() : Func { return this.actionToDo.bind(this); } private getRole(basicAuthUser: string) : string { return "rockstar"; // davegrohl's role fetched from database } actionToDo(i: number, basicAuthUser: string) : boolean { // Database operation here. // Get the role of basicAuthUser console.log(this); var role = this.getRole(basicAuthUser); // davegrohl's role fetched from database return role === this.roleAllowed; } } interface Func { (random: number, basicAuthUser: string) : boolean; } function doSomething(...actions: Func[]) : void { var basicAuthUser = "davegrohl"; // obtained from browser's Basic Authentication for(var i = 0; i < actions.length; ++i) { var a = actions[i]; var okToContinue = a(Math.random() * 100, basicAuthUser); if (!okToContinue) break; } } console.log(''); console.log('without authorization'); doSomething( (i,u) => { console.log('Alpha ' + i); return true; }, (i,u) => { console.log('Beta ' + i); return true; } ); console.log(''); console.log("Rockstar activity:"); doSomething( new Authorizer("rockstar").getBindedActionToDo(), (i,u) => { console.log("Alpha " + i); return true; }, (i,u) => { console.log("Beta " + i); return true; } ); console.log(''); console.log("Guest activity:"); doSomething( new Authorizer("guest").getBindedActionToDo(), (i,u) => { console.log("Alpha " + i); return true; }, (i,u) => { console.log("Beta " + i); return true; } );
Output:

Happy Coding!
No comments:
Post a Comment