inversify-express-utils icon indicating copy to clipboard operation
inversify-express-utils copied to clipboard

Inversify-express-utils: Inject server level middleware

Open RPallas92 opened this issue 7 years ago • 6 comments

I don't see a clear way to inject server level express middleware, if that middleware has HttpContext as dependency.

Expected Behavior

    const expressServer = new InversifyExpressServer(inversifyContainer, null, { rootPath: apiPrefix })
    expressServer.setConfig((app: express.Application) => {
        const authMiddleware = inversifyContainer.get<AuthorizationMiddleware>(TYPES.AuthorizationMiddleware)
        app.use(authMiddleware.middleware())
        
    })

Middleware is injected at setConfig level.

Current Behavior

The following error is thrown: No matching bindings found for serviceIdentifier: Symbol(HttpContext)

RPallas92 avatar May 07 '18 15:05 RPallas92

+1

josete89 avatar May 07 '18 17:05 josete89

+1

jlopezmartinez avatar May 07 '18 17:05 jlopezmartinez

Since HttpContext can only ever exist in a per request scope and at the time that top level middleware is bound, that can't be available, the way that you're trying to do this will probably not be feasible.

Looking at the code, there is no point in time where you can inject a global middleware that can receive per request injections.

https://github.com/inversify/inversify-express-utils/blob/01725a5eb7bbad0de08326472ea10ee134a90424/src/server.ts#L224

Probably wouldn't be a bad idea to implement something like Web API's various filter interfaces like https://msdn.microsoft.com/en-us/library/system.web.http.filters.iauthenticationfilter(v=vs.118).aspx

(Not a maintainer here, just a drive by suggestion)

dcherman avatar May 18 '18 14:05 dcherman

It is possible to derive the HttpContext from the Request at run time, but not from a documented API, so it may cease to work in the future:

Reflect.getMetadata('inversify-express-utils:httpcontext', req);

I use a base middleware with a static method to help register server level middleware. (It manages async handlers as well.) The helper method does need direct access to your DI container to create a new instance of the middleware for each request.

import { NextFunction, Request, Response } from 'express-serve-static-core';
import { Container, injectable } from 'inversify';
import { BaseMiddleware as InversifyBaseMiddleware } from 'inversify-express-utils';

export type MiddlewareFunction = (req: Request, res: Response, next: NextFunction) => void;
export interface IMiddleware {
    handler: MiddlewareFunction;
}

@injectable()
export abstract class BaseMiddleware extends InversifyBaseMiddleware implements IMiddleware {
    public static getHttpContext(req: Request) {
        // FIXME: playing in inversify-express-utils' sandbox
        return Reflect.getMetadata(
            'inversify-express-utils:httpcontext',
            req,
        );
    }

    public static serverHandler(di: Container, middlewareToken: symbol): MiddlewareFunction {
        return (req: Request, res: Response, next: NextFunction) => {
            const middleware = di.get<BaseMiddleware>(middlewareToken);
            // cast to any to allow writing of httpContext
            (middleware as any).httpContext = BaseMiddleware.getHttpContext(req);
            middleware.handler(req, res, next);
        };
    }

    public handler(req: Request, res: Response, next: NextFunction) {
        try {
            const result = this.handle(req, res, next);
            if (result != null && typeof (result as Promise<void>).then === 'function') {
                Promise.resolve(result).catch(err => next(err));
            }
        } catch (err) {
            next(err);
        }
    }

    protected abstract handle(req: Request, res: Response, next: NextFunction): Promise<void> | void;
}

Assuming I have FooMiddlware that derives from BaseMiddleware bound to symbol FooMiddlewareToken, registering it for all requests looks like this:

const fooHandler = BaseMiddleware.serverHandler(container, FooMiddlewareToken);
app.use(fooHandler);

kalahari avatar Aug 13 '19 20:08 kalahari

@notaphplover @dcavanagh do you think we can maybe do something to actually be able to @inject into the setErrorConfig function ?

I don't think we can actually apply any decorators before the .build() function tbh, I'll want to look into this, but I really need a second opinion here.

PodaruDragos avatar Apr 26 '21 13:04 PodaruDragos

Hi @PodaruDragos I need to do some research, I'm almost new to the inversify-express-utils project

notaphplover avatar Apr 30 '21 14:04 notaphplover