Skip to content

Commit

Permalink
feat(nestjs/core): run onApplicationListen hook after listen server.
Browse files Browse the repository at this point in the history
  • Loading branch information
underfin committed Oct 13, 2019
1 parent 61992c7 commit e01b1a8
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 3 deletions.
82 changes: 82 additions & 0 deletions integration/hello-world/e2e/middleware-execute-once.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
Controller,
Get,
INestApplication,
Injectable,
MiddlewareConsumer,
NestMiddleware,
Module,
} from '@nestjs/common';
import { Test } from '@nestjs/testing';
import * as request from 'supertest';
import { expect } from 'chai';

let number: number = 0;
let applyRoute: string;
@Injectable()
class Middleware implements NestMiddleware {
use(req, res, next) {
number++;
applyRoute = req.route.path;
next();
}
}

@Controller('/a')
class TestController {
@Get('/test')
testA() {
return '';
}
@Get('/:id')
testB() {
return '';
}
}

@Module({
controllers: [TestController],
})
class TestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(Middleware).forRoutes(TestController);
}
}

describe('Middleware (execution once)', () => {
let app: INestApplication;

beforeEach(async () => {
number = 0;
applyRoute = '';
app = (await Test.createTestingModule({
imports: [TestModule],
}).compile()).createNestApplication();

await app.init();
});

it(`forRoutes(TestController) should execute middleware once when request url is equal match`, () => {
return request(app.getHttpServer())
.get('/a/test')
.expect(200)
.then(() => {
expect(number).to.be.eq(1);
expect(applyRoute).to.be.eq('/a/test')
});
});

it(`forRoutes(TestController) should execute middleware once when request url not is equal match`, () => {
return request(app.getHttpServer())
.get('/a/1')
.expect(200)
.then(() => {
expect(number).to.be.eq(1);
expect(applyRoute).to.be.eq('/a/:id')
});
});

afterEach(async () => {
await app.close();
});
});
6 changes: 6 additions & 0 deletions packages/common/utils/shared.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ export const isNil = (obj: any): obj is null | undefined =>
isUndefined(obj) || obj === null;
export const isEmpty = (array: any): boolean => !(array && array.length > 0);
export const isSymbol = (fn: any): fn is symbol => typeof fn === 'symbol';
export const getPathName = (originalUrl: string): string => {
const queryParamsIndex = originalUrl.indexOf('?');
return queryParamsIndex >= 0
? originalUrl.slice(0, queryParamsIndex)
: originalUrl;
};
44 changes: 41 additions & 3 deletions packages/core/middleware/middleware-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { MiddlewareBuilder } from './builder';
import { MiddlewareContainer } from './container';
import { MiddlewareResolver } from './resolver';
import { RoutesMapper } from './routes-mapper';
import { getPathName } from '@nestjs/common/utils/shared.utils';

export class MiddlewareModule {
private readonly routerProxy = new RouterProxy();
Expand Down Expand Up @@ -112,6 +113,7 @@ export class MiddlewareModule {
config,
module,
applicationRef,
middlewareConfig,
);
});

Expand All @@ -134,6 +136,7 @@ export class MiddlewareModule {
config: MiddlewareConfiguration,
module: string,
applicationRef: any,
middlewareConfig: MiddlewareConfiguration[],
) {
const { forRoutes } = config;
const registerRouteMiddleware = async (
Expand All @@ -145,6 +148,7 @@ export class MiddlewareModule {
config,
module,
applicationRef,
middlewareConfig,
);
};
await Promise.all(forRoutes.map(registerRouteMiddleware));
Expand All @@ -156,6 +160,7 @@ export class MiddlewareModule {
config: MiddlewareConfiguration,
moduleKey: string,
applicationRef: any,
middlewareConfig: MiddlewareConfiguration[],
) {
const middlewareCollection = [].concat(config.middleware);
const module = this.container.getModuleByKey(moduleKey);
Expand All @@ -176,6 +181,7 @@ export class MiddlewareModule {
routeInfo.path,
module,
collection,
middlewareConfig,
);
}),
);
Expand All @@ -188,6 +194,7 @@ export class MiddlewareModule {
path: string,
module: Module,
collection: Map<string, InstanceWrapper>,
middlewareConfig: MiddlewareConfiguration[],
) {
const { instance, metatype } = wrapper;
if (isUndefined(instance.use)) {
Expand All @@ -196,8 +203,14 @@ export class MiddlewareModule {
const router = applicationRef.createMiddlewareFactory(method);
const isStatic = wrapper.isDependencyTreeStatic();
if (isStatic) {
const proxy = await this.createProxy(instance);
return this.registerHandler(router, path, proxy);
const proxy = await this.createProxy(instance, null, middlewareConfig);
return this.registerHandler(
router,
path,
proxy,
method,
middlewareConfig,
);
}
this.registerHandler(
router,
Expand Down Expand Up @@ -227,6 +240,7 @@ export class MiddlewareModule {
const proxy = await this.createProxy<TRequest, TResponse>(
contextInstance,
contextId,
middlewareConfig,
);
return proxy(req, res, next);
} catch (err) {
Expand All @@ -243,12 +257,15 @@ export class MiddlewareModule {
exceptionsHandler.next(err, host);
}
},
method,
middlewareConfig,
);
}

private async createProxy<TRequest = any, TResponse = any>(
instance: NestMiddleware,
contextId = STATIC_CONTEXT,
middlewareConfig: MiddlewareConfiguration[],
): Promise<(req: TRequest, res: TResponse, next: () => void) => void> {
const exceptionsHandler = this.routerExceptionFilter.create(
instance,
Expand All @@ -268,6 +285,8 @@ export class MiddlewareModule {
res: TResponse,
next: () => void,
) => void,
method: RequestMethod,
middlewareConfig: MiddlewareConfiguration[],
) {
const prefix = this.config.getGlobalPrefix();
const basePath = validatePath(prefix);
Expand All @@ -276,7 +295,26 @@ export class MiddlewareModule {
// and global prefix has been set
path = '*';
}
router(basePath + path, proxy);
const pathname = basePath + path;
router(pathname, (req, res, next) => {
const requestUrl = getPathName(req.originalUrl);
const isEqualMatchMiddleware = requestUrl === pathname;
const isEqualMatchHandler = middlewareConfig.some(
(config: MiddlewareConfiguration) =>
config.forRoutes.some(
route => (route as RouteInfo).path === requestUrl,
),
);
if (
!isEqualMatchHandler ||
method === RequestMethod.ALL ||
(isEqualMatchHandler && isEqualMatchMiddleware)
) {
proxy(req, res, next);
} else {
next();
}
});
}

private registerRequestProvider<T = any>(request: T, contextId: ContextId) {
Expand Down
3 changes: 3 additions & 0 deletions packages/core/test/middleware/middlewares-module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ describe('MiddlewareModule', () => {
configuration,
'Test' as any,
app as any,
[],
),
).to.eventually.be.rejectedWith(RuntimeException);
});
Expand Down Expand Up @@ -126,6 +127,7 @@ describe('MiddlewareModule', () => {
configuration,
moduleKey,
app as any,
[],
),
).to.be.rejectedWith(InvalidMiddlewareException);
});
Expand Down Expand Up @@ -168,6 +170,7 @@ describe('MiddlewareModule', () => {
configuration,
moduleKey,
app as any,
[],
);
expect(createMiddlewareFactoryStub.calledOnce).to.be.true;
});
Expand Down

0 comments on commit e01b1a8

Please sign in to comment.