简体   繁体   中英

How to achieve component scanning/auto discovery of Controllers using Inversify + inversify-express-utils

I am looking for a way to load my controller classes in Inversify that are annotated with @controller() from inversify-express-utils without needing to manage a hand crafted/manually constructed module of imports/exports.

All of the examples I am seeing seem to indicate that in your entry file you need to import each of your controllers explicitely so that inversify-express-utils picks up on the @controller annotations, however this feels like it would be rather tedious for applications of a medium to large size.

In plain JavaScript I would do something like recursively require each file in a directory at runtime, however I understand that due to the way TypeScript works, this isn't exactly possible?

Using the inversify-binding-decorators package, I am able to simply annotate a service with @provide() and that seems to be enough to have the class added to the IOC container, however that does not seem to be enough for inversify-express-utils to discover the @controller() annotations on the class.

Is there a way to achieve auto discovery of these @controller() annotated classes without manually importing them into a entry file?

/services/StarwarsNamesService.ts

import { provide } from 'inversify-binding-decorators';
import * as names from 'starwars-names';
import * as matchSorter from 'match-sorter';

@provide(StarwarsNamesService)
export default class StarwarsNamesService {

    public getNames(): Array<string> {
        return names.all;
    }

    public getRandomNames(count?: number): Array<string> {
        if (!count) { count = 1; }
        return names.random(count);
    }

    public searchNames(term?: string): Array<string> {
        return matchSorter(this.getNames(), term);
    }
}

/controllers/StarWarsNamesController.ts

import { inject } from 'inversify';
import { controller, httpGet } from 'inversify-express-utils';
import StarwarsNamesService from '../../starwars-names/StarwarsNamesService';

@controller('/api/starwars-names')
export default class StarWarsNamesController {

    @inject(StarwarsNamesService)
    private starwarsNamesService: StarwarsNamesService;

    @httpGet('/')
    public getAll(): any {
        return this.starwarsNamesService.getNames();
    }
}

boostrap.ts

import 'reflect-metadata';
import * as express from "express";
import * as path from 'path';
import { InversifyExpressServer } from 'inversify-express-utils';
import { buildProviderModule } from "inversify-binding-decorators";
import { Container } from 'inversify';

// Has to be explicitly imported otherwise is undiscovered
import './controllers/StarwarsNamesController.ts';

let container = new Container();

container.load(buildProviderModule());

let server = new InversifyExpressServer(container);

let serverInstance = server.build();

serverInstance.listen(3000, () => {
    console.log('Server started on port 3000 :)');
});

Thrown Error:

E:\..\..\node_modules\inversify-express-utils\lib\utils.js:10
        throw new Error(constants_1.NO_CONTROLLERS_FOUND);
              ^
Error: No controllers have been found! Please ensure that you have register at least one Controller.
    at Object.getControllersFromContainer
    ...
  1. you must initiate express in async way
  2. you must import controller using require and also in async way, it should : await require(''./controllers/StarwarsNamesController');

You can do the following to expose your controllers

  1. Create index.js or index.ts file in your controller folder
  2. add each controller file like this export * from "./your controller file path"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM