-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Closed
Labels
Description
So our new router is loosely inspired by Express' Router - especially router.handle.
The one thing that never made sense to me is - why is the Router in Express both locating the function to execute and executing it? That really puts the request into a black hole... Instead I think we should do something slightly different (in the spirit of extensibility)...
// loopback core
app.bind(OPERATION, () => {
const ctx = this;
return findOperationForContext(ctx);
});
app.bind(RESULT, () => {
const ctx = this;
const args = ctx.get(OPERATION_ARGS);
const operation = ctx.get(OPERATION);
const controller = ctx.get(CONTROLLER);
return operation.apply(controller, args));
});
class Application {
public async handle(ctx) {
const result = await ctx.get(RESULT);
const writer = ctx.get(RESPONSE_WRITER);
writer(result); // basically res.write(JSON.stringify(result))
}
}The benefits of this approach is that we build on top of a consistent overridable dependency resolution.
- You can always rebind what we've bound for you by default (eg. rebind
RESULT,OPERATION, anything...) for mocking or extensibility - We can add performance, debugging, and other enhancements that will also be picked up by the router if it also uses this same approach
Also consider how this could make it easier to implement extensions eg. Authorization...
app.bind(Bindings.AUTHORIZATION).to(() => {
const ctx = this;
const role = await ctx.get('currentUser.role');
const operation = ctx.get(OPERATION); // <====
const acl = ctx.get('${operation.name}.acl'); // <====
for (ctrl of acl) {
if (ctrl.denies(role) || ctrl.denies(ctx)) return ctrl.denial(ctx);
}
return new Authorization(Authorization.DENY, 'you do not have permission to...');
});