使用InversifyJS注入函数的最佳方法

时间:2017-05-11 15:46:36

标签: javascript node.js typescript dependency-injection inversifyjs

使用official recipe注入函数有InversifyJS。基本上,我们定义一个辅助函数,它将返回给定函数func的curried版本,并使用container.get(...)解析其所有依赖项:

import { container } from "./inversify.config"

function bindDependencies(func, dependencies) {
    let injections = dependencies.map((dependency) => {
        return container.get(dependency);
    });

    return func.bind(func, ...injections);
}

export { bindDependencies };

我们这样使用它:

import { bindDependencies } from "./utils/bindDependencies";
import { TYPES } from "./constants/types";

function testFunc(something, somethingElse) {
    console.log(`Injected! ${something}`);
    console.log(`Injected! ${somethingElse}`);
}

testFunc = bindDependencies(testFunc, [TYPES.something, TYPES.somethingElse]);

export { testFunc };

我想自动注入函数,而不是明确地将它的依赖性提供给bindDependencies,可能是基于函数的参数名称。像这样:

import { default as express, Router } from 'express';

import { bindDependencies } from '../injector/injector.utils';

import { AuthenticationMiddleware } from './authentication/authentication.middleware';
import { UsersMiddleware } from './users/users.middleware';

import { ENDPOINTS } from '../../../../common/endpoints/endpoints.constants';


function getRouter(
    authenticationMiddleware: AuthenticationMiddleware,
    usersMiddleware: UsersMiddleware,
): express.Router {
    const router: express.Router = Router();

    const requireAnonymity: express.Handler = authenticationMiddleware.requireAnonymity.bind(authenticationMiddleware);
    const requireAuthentication: express.Handler = authenticationMiddleware.requireAuthentication.bind(authenticationMiddleware);

    router.route(ENDPOINTS.AUTHENTICATION)
        .put(requireAnonymity, authenticationMiddleware.login.bind(authenticationMiddleware))
        .delete(requireAuthentication, authenticationMiddleware.logout.bind(authenticationMiddleware));

    router.route(ENDPOINTS.USER)
        .put(requireAnonymity, usersMiddleware.register.bind(usersMiddleware))
        .post(requireAuthentication, usersMiddleware.update.bind(usersMiddleware))
        .delete(requireAuthentication, usersMiddleware.remove.bind(usersMiddleware));

    return router;
}

const router: express.Router = invoke(getRouter);

export { router as Router };

注意在这种情况下我只想调用一次注入函数并得到它的返回值,这就是我要导出的内容,所以也许有更好的方法可以在不将代码包装到函数中的情况下执行此操作,但是我虽然直接在我的合成根之外使用container.get(...)并不是一个好主意,因为这个模块的依赖关系不明确,可能会分散在其所有行中。此外,导出该功能将简化测试。

回到我的问题,我的invoke函数如下所示:

function invoke<T>(fn: Function): T {
    const paramNames: string[] = getParamNames(fn);

    return fn.apply(null, paramNames.map((paramName: string)
        => container.get( (<any>container).map[paramName.toUpperCase()] ))) as T;
}

对于getParamNames,我使用了此处提出的解决方案之一:How to get function parameter names/values dynamically from javascript

(<any>container).map是我在创建容器后在inversify.config.ts创建的对象,它链接所有依赖关键字的字符串表示和真实密钥,无论其类型如何(在本例中,只是symbolFunction):

const container: Container = new Container();

container.bind<FooClass>(FooClass).toSelf();

...

const map: ObjectOf<any> = {};

(<any>container)._bindingDictionary._map
    .forEach((value: any, key: Function | symbol) => {
        map[(typeof key === 'symbol'
            ? Symbol.keyFor(key) : key.name).toUpperCase()] = key;
    });

(<any>container).map = map;

任何人都知道是否有更好的方法可以做到这一点,或者是否有任何重要原因不这样做?

1 个答案:

答案 0 :(得分:1)

使用函数参数名称的主要问题是压缩代码时可能出现的问题:

function test(foo, bar) {
    console.log(foo, bar);
}

变为:

function test(a,b){console.log(a,b)}

因为你在Node.js应用程序中工作,所以你可能没有使用压缩,所以这不应该是你的问题

我认为您的解决方案是一个很好的时间解决方案。如果您查看TypeScript Roapmap,请在“未来”部分中看到:

  
      
  • 函数表达式/箭头函数的装饰器
  •   

这意味着将来InversifyJS将允许您执行以下操作:

  

注意:假设AuthenticationMiddlewareUsersMiddleware

@injectable()
function getRouter(
    authenticationMiddleware: AuthenticationMiddleware,
    usersMiddleware: UsersMiddleware,
): express.Router {
    const router: express.Router = Router();

    const requireAnonymity: express.Handler = authenticationMiddleware.requireAnonymity.bind(authenticationMiddleware);
    const requireAuthentication: express.Handler = authenticationMiddleware.requireAuthentication.bind(authenticationMiddleware);

    router.route(ENDPOINTS.AUTHENTICATION)
        .put(requireAnonymity, authenticationMiddleware.login.bind(authenticationMiddleware))
        .delete(requireAuthentication, authenticationMiddleware.logout.bind(authenticationMiddleware));

    router.route(ENDPOINTS.USER)
        .put(requireAnonymity, usersMiddleware.register.bind(usersMiddleware))
        .post(requireAuthentication, usersMiddleware.update.bind(usersMiddleware))
        .delete(requireAuthentication, usersMiddleware.remove.bind(usersMiddleware));

    return router;
}

或以下:

  

注意:假设AuthenticationMiddlewareUsersMiddleware 接口

@injectable()
function getRouter(
    @inject("AuthenticationMiddleware") authenticationMiddleware: AuthenticationMiddleware,
    @inject("UsersMiddleware") usersMiddleware: UsersMiddleware,
): express.Router {
    const router: express.Router = Router();

    const requireAnonymity: express.Handler = authenticationMiddleware.requireAnonymity.bind(authenticationMiddleware);
    const requireAuthentication: express.Handler = authenticationMiddleware.requireAuthentication.bind(authenticationMiddleware);

    router.route(ENDPOINTS.AUTHENTICATION)
        .put(requireAnonymity, authenticationMiddleware.login.bind(authenticationMiddleware))
        .delete(requireAuthentication, authenticationMiddleware.logout.bind(authenticationMiddleware));

    router.route(ENDPOINTS.USER)
        .put(requireAnonymity, usersMiddleware.register.bind(usersMiddleware))
        .post(requireAuthentication, usersMiddleware.update.bind(usersMiddleware))
        .delete(requireAuthentication, usersMiddleware.remove.bind(usersMiddleware));

    return router;
}