Inversify-express-utils: Inject server level middleware
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)
+1
+1
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)
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);
@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.
Hi @PodaruDragos I need to do some research, I'm almost new to the inversify-express-utils project