一般的启动脚本是这样的
import { NestFactory } from '@nestjs/core';
import { ApplicationModule } from './app.module';
import { ValidationPipe } from './common/pipes/validation.pipe';
async function bootstrap() {
const app = await NestFactory.create(ApplicationModule);
await app.listen(3000);
}
bootstrap();
第一步:创建NestApplication实例
src.core/nest-factory.ts
//创建一个NestApplication实例,使用指定根模块类,与Express对象
public async create(module, express = ExpressAdapter.create()): Promise {
//初始化根模块,进行模块依赖的提取、实例化
await this.initialize(module);
//创建应用程序类,返回一个代理对象,可以捕捉异常,打印异常
return this.createNestInstance(
//一个应用程序类,使用容器、express创建
new NestApplication(this.container, express),
);
}
//创建所有的nest实例
private createNestInstance(instance: T) {
//创建代理对象
return this.createProxy(instance);
}
//创建指定实例的代理
private createProxy(target) {
//创建处理函数
const proxy = this.createExceptionProxy();
//创建代理对象,get、set操作都被处理函数拦截
return new Proxy(target, {
get: proxy,
set: proxy,
});
}
//创建处理函数
private createExceptionProxy() {
//receiver是被代理的实例,prop为要访问或者设置的属性名
return (receiver, prop) => {
//如果属性不存在直接返回
if (!(prop in receiver))
return;
//如果属性为函数
if (isFunction(receiver[prop])) {
//返回新函数
return (...args) => {
let result;
//在异常区内同步运行,这个区可以捕捉异常
ExceptionsZone.run(() => {
//运行原有函数
result = receiver[prop](...args);
});
return result;
};
}
//返回源属性或者新函数
return receiver[prop];
};
}
//初始化模块
private async initialize(module) {
try {
//打印应用程序启动信息
this.logger.log(messages.APPLICATION_START);
//在异常区异步运行
await ExceptionsZone.asyncRun(async () => {
//使用依赖扫描器扫描模块,获取所有关联模块、控制器、组件,并且存储他们
this.dependenciesScanner.scan(module);
//使用实例加载器创建依赖的实例
await this.instanceLoader.createInstancesOfDependencies();
});
}
//遇到异常取消进程
catch (e) {
process.abort();
}
}
创建NestApplication实例也分为两步:
1.初始化模块:在可捕获异常区域中进行依赖扫描、实例加载
2.创建Nest实例:创建一个可捕获异常代理的实例
特点:初始化时的方法调用、创建的Nest实例方法都是在一个异常区域中运行的,出现的异常会被框架处理
第二部:启动监听
src/core/nest-application.ts
//重载监听方法
public async listen(port: number, callback?: () => void);
public async listen(port: number, hostname: string, callback?: () => void);
//实现监听方法
public async listen(port: number, ...args) {
//如果未初始化先初始化
(!this.isInitialized) && await this.init();
//使用内部的原生服务器实例监听端口,这个实例使用express构造
this.httpServer.listen(port, ...args);
return this.httpServer;
}
//初始化方法
public async init() {
//设置解析中间件
this.setupParserMiddlewares();
//设置模块,即Socket、中间件、微服务模块
await this.setupModules();
await this.setupRouter();
//调用初始化钩子
this.callInitHook();
this.logger.log(messages.APPLICATION_READY);
//修改标志
this.isInitialized = true;
}
//设置解析中间件,这个中间件并不是那个意思
public setupParserMiddlewares() {
const parserMiddlewares = {
//json解析器
jsonParser: bodyParser.json(),
//urlencoded编码解析器
urlencodedParser: bodyParser.urlencoded({ extended: true }),
};
//遍历,过滤出没使用的中间件,添加到express上
Object.keys(parserMiddlewares)
.filter((parser) => !this.isMiddlewareApplied(this.express, parser))
.forEach((parserKey) => this.express.use(parserMiddlewares[parserKey]));
}
//设置模块,中间件、socket、微服务
public async setupModules() {
//设置SocketModule,将容器与配置传递给它
this.socketModule && this.socketModule.setup(this.container, this.config);
//设置微服务模块,还是将容器与配置传递给它
if (this.microservicesModule) {
this.microservicesModule.setup(this.container, this.config);
this.microservicesModule.setupClients(this.container);
}
//设置中间件模块,这一步调用了模块实例的configure方法,生成了中间件配置信息,存储到了中间件容器中
await this.middlewaresModule.setup(
this.middlewaresContainer,
this.container,
this.config,
);
}
//设置路由
public async setupRouter() {
//创建mergeParams: true 路由
const router = ExpressAdapter.createRouter();
//为创建的路由设置中间件,根据上面得到的中间件容器中的配置信息
await this.setupMiddlewares(router);
//解析路由,将得到的执行上下文方法与路由路径关联到router对象上,执行上下文包含了参数注入、Guard、拦截器、Pipe、异常过滤器等功能
this.routesResolver.resolve(router);
//为路由对象加上全局前缀,设置到express实例上,router对象上所有路由路径都会加上这个统一前缀
this.express.use(validatePath(this.config.getGlobalPrefix()), router);
}
在listen方法中对NestApplication实例进行初始化,分为以下步骤
1.设置请求解析中间件:对所有路由设置了两个请求解析中间件,application/json与application/x-www-form-urlencoded这两个MIME类型的请求体会被解析,也就是说这两个类型请求获取不到原始请求体,req.body中直接就是对象
2.设置模块:这里的模块不是Module实例,而是内置的模块,有三个:Middleware、WebSocket、Microservices三个模块,分别管理了中间件、websocket、微服务三个功能,这一步设置模块主要是初始化了相应的模块