Loopback-next: The key controller.current.ctor was not bound to any value.

Created on 19 Mar 2018  路  12Comments  路  Source: strongloop/loopback-next

Description / Steps to reproduce / Feature proposal:

Hi everyone,
I added @loopback/authentication to my LoopBack 4 application (like descriped in README.md and I always get the error: "The key controller.current.ctor was not bound to any value.". Does anyone know what I have to do, any hint, where I have to look? Thanks in advance!

Current Behavior:

Every time, when I call my TestController: curl -u username:password http://localhost:3000/whoami. I get the following error: Error: The key controller.current.ctor was not bound to any value.

developer-experience p1

Most helpful comment

I have the same issue here, and I solved it in other way. The exact error is:

Unhandled error in GET /: 500 Error: The key controller.current.ctor was not bound to any value.

Problem with previous try/catch solution (from @NiklasA https://github.com/strongloop/loopback-next/issues/1144#issuecomment-374775965): It'll fails with static routes (If you use them), because it puts the this.findRoute inside the try block.

My final solution: After following this tutorial for authentication, I figured out that the problem only occurs when you try to get a static route. My solution was to check if the requested route is static (or not) before do the this.authenticateRequest(request) call.

This is my final (for now) _sequence.ts_:

import { inject } from '@loopback/context';
import {
  FindRoute,
  InvokeMethod,
  ParseParams,
  Reject,
  RequestContext,
  RestBindings,
  Send,
  SequenceHandler,
  StaticAssetsRoute,
} from '@loopback/rest';
import { AuthenticationBindings, AuthenticateFn } from '@loopback/authentication';
import { VerifyUserRoleBindings, VerifyUserRoleFn, UserRoleData } from './components/verify-user-role';

const SequenceActions = RestBindings.SequenceActions;

export class MySequence implements SequenceHandler {
  constructor(
    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,
    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,
    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,
    @inject(SequenceActions.SEND) public send: Send,
    @inject(SequenceActions.REJECT) public reject: Reject,
    @inject(AuthenticationBindings.AUTH_ACTION) protected authenticateRequest: AuthenticateFn,
    @inject(VerifyUserRoleBindings.ROLE_ACTION) protected verifyUserRole: VerifyUserRoleFn,
  ) { }

  async handle(context: RequestContext) {
    try {
      const { request, response } = context;
      const route = this.findRoute(request);

      // !!IMPORTANT: authenticateRequest fails on static routes!
      if (!(route instanceof StaticAssetsRoute)) {
        // Verify authentication cases
        const user = await this.authenticateRequest(request);

        // Verify user roles cases
        await this.verifyUserRole(<UserRoleData>user)
      }

      const args = await this.parseParams(request, route);
      const result = await this.invoke(route, args);
      this.send(response, result);
    } catch (err) {
      this.reject(context, err);
    }
  }
}

All 12 comments

@NiklasA It would be helpful if you can either describe steps to reproduce the problem or having a sandbox repo to contain the test app.

Hi @raymondfeng,

I found my issue. The problem was that I integrated @loopback/authentication and example-log-extension both and that I merged both snippets wrong in sequence.ts. I had to move await this.authenticateRequest(req); into try / catch block.

Before:

  async handle(req: ParsedRequest, res: ServerResponse) {
    // we define these variable outside of try / catch so that they can be accessed by logger.
    let args: any = [];
    let result: any;

    // this is the important line added to the default sequence implementation.
    await this.authenticateRequest(req);

    // optionally start timing the sequence using the timer function available via LogFn.
    const start = this.logger.startTimer();

    try {
      const route = this.findRoute(req);

After:

  async handle(req: ParsedRequest, res: ServerResponse) {
    // we define these variable outside of try / catch so that they can be accessed by logger.
    let args: any = [];
    let result: any;

    // optionally start timing the sequence using the timer function available via LogFn.
    const start = this.logger.startTimer();

    try {
      const route = this.findRoute(req);

      // this is the important line added to the default sequence implementation.
      await this.authenticateRequest(req);

The second mistake was, that I injected @inject(RestBindings.Http.CONTEXT) public ctx: Context in constructor of MySequence. Do you know, why it is not possible to inject Context, when I use @loopback/authentication?

Thank you in advance.
Niklas

Please note we use a hierarchy of contexts, for example, a request context is created per http request and it uses the app context as the parent. Binding controller.current.ctor is only available for the request ctx. The injection of context at constructor level will get the app context if it's resolved there.

@raymondfeng @dhmlau @NiklasA Hello, what's the status of this issue? What actions are needed before we can closed this as done?

Hi @bajtos, it can be closed, I made a mistake described in comment above.

I have the same issue here, and I solved it in other way. The exact error is:

Unhandled error in GET /: 500 Error: The key controller.current.ctor was not bound to any value.

Problem with previous try/catch solution (from @NiklasA https://github.com/strongloop/loopback-next/issues/1144#issuecomment-374775965): It'll fails with static routes (If you use them), because it puts the this.findRoute inside the try block.

My final solution: After following this tutorial for authentication, I figured out that the problem only occurs when you try to get a static route. My solution was to check if the requested route is static (or not) before do the this.authenticateRequest(request) call.

This is my final (for now) _sequence.ts_:

import { inject } from '@loopback/context';
import {
  FindRoute,
  InvokeMethod,
  ParseParams,
  Reject,
  RequestContext,
  RestBindings,
  Send,
  SequenceHandler,
  StaticAssetsRoute,
} from '@loopback/rest';
import { AuthenticationBindings, AuthenticateFn } from '@loopback/authentication';
import { VerifyUserRoleBindings, VerifyUserRoleFn, UserRoleData } from './components/verify-user-role';

const SequenceActions = RestBindings.SequenceActions;

export class MySequence implements SequenceHandler {
  constructor(
    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,
    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,
    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,
    @inject(SequenceActions.SEND) public send: Send,
    @inject(SequenceActions.REJECT) public reject: Reject,
    @inject(AuthenticationBindings.AUTH_ACTION) protected authenticateRequest: AuthenticateFn,
    @inject(VerifyUserRoleBindings.ROLE_ACTION) protected verifyUserRole: VerifyUserRoleFn,
  ) { }

  async handle(context: RequestContext) {
    try {
      const { request, response } = context;
      const route = this.findRoute(request);

      // !!IMPORTANT: authenticateRequest fails on static routes!
      if (!(route instanceof StaticAssetsRoute)) {
        // Verify authentication cases
        const user = await this.authenticateRequest(request);

        // Verify user roles cases
        await this.verifyUserRole(<UserRoleData>user)
      }

      const args = await this.parseParams(request, route);
      const result = await this.invoke(route, args);
      this.send(response, result);
    } catch (err) {
      this.reject(context, err);
    }
  }
}

Good catch, @NiklasA. I was aware that we may break the applications assuming that 404 is thrown by findRoute when we change our router to throw the error from invoke, but wasn't sure. However, I think this isn't fixable at our side, since the authentication tutorial is correctly showing that all sequence actions should be wrapped in the try/catch block, not the findRoute action only.

Perhaps we should improve the documentation on routing and findRoute action to make it clear that when static assets are registered, then 404 is thrown by invoke?

Most importantly, I think we need to fix our reference authenticateRequest function to correctly support StaticAssetsRoute and any other custom route that apps or extensions my register via app.route() API.

Is my assessment correct?

@dhmlau I am proposing to open new issue(s) for each of the logical change/fix we need to make, and then close this issue again. Having a fine-grained user stories is easier for planning and keeping track of our progress.

@jormar answer solved my issue too.

same problem here, @jormar answer solved it.

it's VERY important the @jormar information about static routes. The code at https://github.com/strongloop/loopback-next/tree/master/packages/authentication worked before the recent update of loopback 4, that now the root is mapped to /public.

FYI this should be fixed in https://github.com/strongloop/loopback-next/pull/2218

Released as @loopback/[email protected]
Let me know if updating to that version still doesn't solve the bug.

I am closing this issue as solved. Feel free to reopen it if you still run into the same problem
Please see solution details in the comment above ^.
Thanks.

Was this page helpful?
0 / 5 - 0 ratings