[一期

您所在的位置:网站首页 修改上传文件允许类型是什么 [一期

[一期

2023-05-16 02:42| 来源: 网络整理| 查看: 265

本文概要和目录

接上节代码,我们已经构建了gitlab CI现在我们开始唠嗑唠嗑,偏向后端的知识 ,本章内容主要是向大家介绍Nestjs(一个类似于 Spring、 Angular 的Nodejs框架)的基础使用。为什么要去发这篇 这篇文章呢,主要是NestJS官方文档有点很多东西说的不是很明白,例子也比较少且不完整,于是我打算弄来个比较全面的解读,构建一个比较完整的后端应用,汰!开始

重要提醒!:请不要照着文章照抄,建议你先阅读通篇,了解全貌之后再去实践。

image.png

统一返回体

我们回顾我们的目前的应用 不难发现,在返回体Res上我们比价混乱没有一致的返回格式有的这杨,有的那样。是时候改变了,让我们使用interceptor 改造他们

新建一个拦截器去处理

我们默认你已经学习并且掌握了 Interceptor拦截器的有所知识点,如果你还不能掌握 请前往 我的另一片文章 // http-req.interceptor.ts import { Injectable, NestInterceptor, ExecutionContext, CallHandler, } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; export interface Response { data: T; } @Injectable() export class HttpReqTransformInterceptor implements NestInterceptor { intercept( context: ExecutionContext, next: CallHandler, ): Observable { return next .handle() .pipe(map((data) => ({ data, code: 200, msg: '', success: true }))); // 我们非常的单纯把 返回体统一进行了包装 ,不要学我哈,我这里就是为了写而瞎写的,具体的返回code是什么。以及其他的内容 你需要判断,自己设计逻辑。 } }

如何使用

就像使用 和其他任何 Interceptor 一样去使用就好了 @ApiTags('Tag相关') @ApiBearerAuth() @Controller('tag') @UseInterceptors(new HttpReqTransformInterceptor()) // 统一返回体 export class TagController { constructor(private readonly tagService: TagService) {} @UseGuards(AuthGuard('jwt')) @Get('/tags') async getAll() { const value = await this.tagService.getAll(); return value; } @UseGuards(AuthGuard('local')) @Post() async createTag(@Body() tagInfo: Tag) { const value = await this.tagService.create(tagInfo); return value; } @Put('/:id') async updateTag(@Param() params: InterParams, @Body() tagInfo: Tag) { const value = await this.tagService.updateById(params.id, tagInfo); return value; } @Delete('/:id') async deleteTag(@Param() params: InterParams) { const value = this.tagService.deleteById(params.id); return value; } }

最后你将会得到下面这样的返回

{ "data": [], "code": 200, "msg": "", "success": true }

和Dto冲突了怎么办?认证 冲突了怎么办?

细心的小伙伴。已经发现了上面的问题了,那就是,如果我没有验证通过或者我的引应用程序发送了错误🙅‍♂️ 出BUG啦,那么你将会得到下面的返回体

// 我们故意不传递 正常的 验证信息 { "statusCode": 401, "error": "Unauthorized", "msg": "Client Error" } // 写一个程序bug { "statusCode": 500, "msg": "Service Error: Error: xxx" } // 我们期待的是 { "data": [], "code": 200, "msg": "", "success": true } // 这里的解决方案是把 制造这种异常的制造者干掉!,当然你也可以把你自己干掉,你就修改成和Nest系统内置的保存一样的接口类型就好了 // 首先是我们要处理所有的异常过滤器让他们变成我们所期待的样子 // 我们把所有的异常过滤器都修改成统一的返回数据结构体 //AllExceptionsFilter response.status(status).json({ code: status, message: exception.message, data: null, success: false, }); } // HttpExceptionFilter response.status(status).json({ code: status, message: exception.message, data: null, success: false, }); // 同时你还需要在返回的时候进行描述,当然你可以不必,因为过滤器把这件事都处理了,有的同学说,这么这里讲 // 劈叉了呢不是拦截器吗怎么就变成过滤器了?原因很简单,因为全局最上层有Filter 错误处理都在fillter 所以要同时 // 处理Filter 和 Interceptor的关系 //如果你代码中有抛Error的地方加上你的特殊标记 throw new UnauthorizedException('您账户已经在另一处登陆,请重新登陆'); 上传文件

上传文件无疑是非常常见的功能了,我们来看看在Nest中如何做这件事情,(我们有轮子 不怕!🤪)

MulterModule模块

这个是Nestjs提供的一个@nestjs/platform-express 专门用来处理文件,它的使用非常的简单

MulterModule.registerAsync({ imports: [ConfigModule], useFactory: async (configService: ConfigService) => ({ storage: diskStorage({ // 配置存储 destination: join(__dirname, '../../../', '/upload'), // 要存储的位置 filename: (req, file, cb) => { // 文件名处理 const filename = `${randomUUID()}.${file.mimetype.split('/')[1]}`; return cb(null, filename); }, }), }), inject: [ConfigService], }), FileInterceptor UploadedFile 装饰器

仅仅是单上面的内容我们上没办法处理全局的逻辑的,我们需要一个路有来接受参数

// FileInterceptor是从@nestjs/platform-express 导入的一个装饰器,这里是用来处理单个文件📃 @UseInterceptors(FileInterceptor('file')) @Post('upload') uploadFile(@Body() body: any, @UploadedFile() file: Express.Multer.File) { return { file: file.filename, path: file.path, // 路径请结合前面的main多静态目录来实现 我们只返回文件的相对路径, // 为了让外部能够访问 你需要再这里拼上 service 部署的domian地址, size: file.size, }; } // 上述就是一个非常简单的 上传文件了,用form-data格式上传文件上来就好了,下面的例子是关于多文件上传的 @UseInterceptors(FilesInterceptor('files')) @Post('uploads') uploadFiles( @Body() body: any, @UploadedFiles() files: Array, ) { return files.map((item) => ({ name: item.fieldname, path: item.path, size: item.size, })); } 第三方上传阿里云OSS

有的时候我们不仅仅需要上传到自己的服务器,我们还需要上传到第三方的OSS,在Nest中我们可以集成aliyun的OSS-SDK来做到这个功能,在这里我就不详细的说明 阿里云OSS的操作了 你可以直接去看他们的官方文档 help.aliyun.com/document_de… 我们要的是你的额验证凭据,有了凭据你就能操作它们的OSS了,并且阿里云OSS还提供了Nodejs的SDK 集成如下,并且对于到底是把 流 存本地还是存内存 其实各有各的处理方案,我这里把他们存本地了,后期搭配Job定时去清理这些文件

import { HttpService } from '@nestjs/axios'; import { Body, Controller, Get, Post, Render, UploadedFile, UploadedFiles, UseInterceptors, Req, Res, Param, } from '@nestjs/common'; import { Request } from 'express'; import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express'; import * as OSS from 'ali-oss'; import multer, { diskStorage } from 'multer'; import path, { join, normalize } from 'path'; import { randomUUID } from 'crypto'; @Controller('files') export class FilesController { oss: OSS; constructor(private readonly httpService: HttpService) { this.oss = new OSS({ region: 'oss-cn-beijing', accessKeyId: 'yourID', accessKeySecret: 'yourKey', bucket: 'yourBucket', }); } @UseInterceptors(FileInterceptor('file')) @Post('upload') uploadFile(@Body() body: any, @UploadedFile() file: Express.Multer.File) { return { file: file.filename, path: file.path, size: file.size, // 路径请结合前面的main多静态目录来实现 }; } @UseInterceptors(FilesInterceptor('files')) @Post('uploads') uploadFiles( @Body() body: any, @UploadedFiles() files: Array, ) { return files.map((item) => ({ name: item.fieldname, path: item.path, size: item.size, })); } @Post('upload-oss') @UseInterceptors( FileInterceptor('file', { storage: diskStorage({ destination: join(__dirname, '../../../', '/upload-oos'), filename: (req, file, cb) => { const filename = `${randomUUID()}.${file.mimetype.split('/')[1]}`; return cb(null, filename); }, }), }), ) async oos(@UploadedFile() file: Express.Multer.File) { // 上传的时候我们运行你上传到内存中 然后发送给第三方但是这样做不好, // 如果存储文件太多或者并非量 你的机器会撑不住,因此我们建议的做法是先存到某 // 临时目录,然后调用第三方去upload 最后由定时job删除这个up目录就好了 // 主要还是文件的上传和下载 上传比较简单 const value = await this.oss.put(file.filename, normalize(file.path)); return value; } // 启用oss 下载需要做临时验证 @Get('upload-oss/:file') async getOSSFile(@Param() params: { file: string }) { // 上传的时候我们运行你上传到内存中 然后发送给第三方但是这样做不好, // 如果存储文件太多或者并非量 你的机器会撑不住,因此我们建议的做法是先存到某 // 临时目录,然后调用第三方去upload 最后由定时job删除这个up目录就好了 // 主要还是文件的上传和下载 上传比较简单 const value = this.oss.signatureUrl(params.file, { expires: 3600, }); return { url: value, }; } 上面说明了如何存储文件那么如何访问文件呢?

我们需要使用 express.static 来达到这个功能

// main.ts // 配置文件访问 文件夹为静态目录,以达到可直接访问下面文件的目的 const rootDir = join(__dirname, '..'); app.use('/static', express.static(join(rootDir, '/upload'))); // app.use('/static', express.static(join(rootDir, '/upload'))); // 允许配置多个 请求转发 请求转发相对的简单 // module ---省略部分代码(如果你不知道我在写什么,那么你一定么有好好的阅读我前面的文章)--- HttpModule.register({ timeout: 5000, maxRedirects: 5, }), // httpService是nest内置的一个axios模块 使用前需要去module注入 利用这个我们可以去 “爬”人家的数据了 🐶🐶🐶 @Get('httpUser') async getIpAddress() { const value = await this.httpService .get('https://api.gmit.vip/Api/UserInfo?format=json') .toPromise(); return { ...value.data }; } 定时Job

前面我们提过一嘴“要把多余的没有用的上传文件删除掉,在把日志给处理掉”,这些实现都离不开job,我们现在来说说Nest中如何做job

理论知识

首先你需要了解job这个概念和一些常见知识

定时任务允许你按照指定的日期/时间、一定时间间隔或者一定时间后单次执行来调度。 从字面上也提出好理解就是一个job 一个现定时/不定时的工作项任务,可以重复执行的。我们对它的定时衍生出来了一套规定和语法,在Linux世界中,这经常通过操作系统层面的cron包等执行。

名称含义* * * * * *每秒45 * * * * *每分钟第 45 秒_ 10 _ * * *每小时,从第 10 分钟开始0 _/30 9-17 _ * *上午 9 点到下午 5 点之间每 30 分钟0 30 11 * * 1-5周一至周五上午 11:30

我们看看在nest中如何声明job

在Nestjs中我们使用 @nestjs/schedule 这个库它底层上对node-cron的封装,

// 最简单的job ,(它在你app启动之后,会自动的每45s执行一次) // 使用前别忘了去注入 import { Module } from '@nestjs/common'; import { ScheduleModule } from '@nestjs/schedule'; @Module({ imports: [ScheduleModule.forRoot()], }) export class AppModule {} // 启动一个服务去使用它 @Injectable() export class TasksService { @Cron('45 * * * * *') handleCron() { console.log('666') } } // 它还有很多骚操作比如定义间隔 就像定时器,定义延时的话可以调用这个装饰器 :“@Timeout(5000)” @Interval(10000) handleInterval() { this.logger.debug('Called every 10 seconds'); } 如何运行它?

上面的我们提到过这个是自动运行的,那么我们有没有可能手动的去启动它,停止它呢?解析来揭晓

// 手动运行 @Cron('30 * * * * *', { name: 'notifications', }) handleTimeout() { console.log('66666'); } // 在某个 controller/service 中进行手动调度测试 constructor( private schedulerRegistry: SchedulerRegistry ) {} @Get('/job') async stopJob(@Param() params: { start: boolean }) { const job = this.schedulerRegistry.getCronJob('notifications'); // this.schedulerRegistry 更多详细操作请去看官方文档 https://docs.nestjs.cn/7/techniques?id=%e5%ae%9a%e6%97%b6%e4%bb%bb%e5%8a%a1 if (params.start) { job.start(); console.log(job.lastDate()); } else { job.stop(); } } 实践指南 为了简单起见我们这里只设置了一个job(它用来清理上传到OSS的没有的文件) // 使用前 你需要去 module中进行注入哈 import { Module } from '@nestjs/common'; import { JobService } from './job.service'; // 这个模块专门处理job 如果其他模块由job的需求,全收敛到这里来处理 @Module({ imports:[ ScheduleModule.forRoot() ] providers: [], controllers: [], exports: [JobService], }) export class JobModule {} import { Injectable } from '@nestjs/common'; import { Cron, CronExpression, Interval, Timeout } from '@nestjs/schedule'; import * as fs from 'fs'; import { join } from 'path'; // 清除日志目录和本地上传的文件oss临时文件 @Injectable() export class JobService { emptyDir = (fileUrl) => { const files = fs.readdirSync(fileUrl); //读取该文件夹 files.forEach(function (file) { const stats = fs.statSync(fileUrl + '/' + file); if (stats.isDirectory()) { this.emptyDir(fileUrl + '/' + file); } else { fs.unlinkSync(fileUrl + '/' + file); } }); }; // 每天晚上11点执行一次 @Cron(CronExpression.EVERY_DAY_AT_11PM) handleCron() { // 删除OSS文件和日志文件 const OSSRootDir = join(__dirname, '../../../upload-oos'); // 日志一般是转存 而不是删除哈,注意 这里只是简单的例子而已 const accesslogDir = join(__dirname, '../../../logs/access'); const appOutDir = join(__dirname, '../../../logs/app-out'); const errorsDir = join(__dirname, '../../../logs/errors'); this.emptyDir(OSSRootDir); this.emptyDir(accesslogDir); this.emptyDir(appOutDir); this.emptyDir(errorsDir); } } 上swagger

swagger 就非常的简单了直接上代码 官方文档在这里 docs.nestjs.cn/7/recipes?i… 虽然文档这额里很简单,我在这里记录一下我的遇到的坑

main中加入 // 构建swagger文档 const options = new DocumentBuilder() .setTitle('Base-Http-example') .addBearerAuth() .setDescription('一个完善的HttpNodejs服务') .setVersion('1.0') .addTag('Http') .build(); const document = SwaggerModule.createDocument(app, options); SwaggerModule.setup('api', app, document); 去Controller中绑定 @ApiTags('User相关') @Controller('user') @UseInterceptors(new HttpReqTransformInterceptor()) // 统一返回体 export class UserController { ....省略很多代码,Swagger用法在官方已经描述得非常详细了。各种都有也没什么坑 } 去Dto中写参数说明 也就是写Model export class UserInfoDTO { @ApiProperty({ description: '名称', default: '用户1', }) @IsNotEmpty({ message: '用户名不允许为空' }) username: string; @ApiProperty() @IsNotEmpty({ message: '密码不允许为空' }) password: string; @ApiProperty() @IsNotEmpty({ message: '更新创建时间必选' }) @IsNumber() update_time: number; @ApiProperty() @IsNotEmpty({ message: '创建时间必选' }) create_time: number; @ApiProperty() @IsNotEmpty({ message: '状态必填' }) state: number; }

上面都是最基础的用法,如果还希望有跟多的骚气操作请前去官方文档

利用redis做单点登

接下来我们将会说明,如何用。redis做单点登录

首先是安装redis

这个应该对各位来说是比较的简单的哈,可以直接去看菜鸟教程 www.runoob.com/redis/redis…

我们自己实现一个Module

在我们的moduels中实现三个文件 他们分别是cache 的controller module和service (实际上我们只需要service就好了哈哈哈)

@Controller('cache') export class CacheController {} @Module({ imports: [], controllers: [CacheController], providers: [CacheService], exports: [CacheService], }) export class CacheModule {} import { Injectable } from '@nestjs/common'; import RedisC, { Redis } from 'ioredis'; @Injectable() // 目前的版本比较的简单 只是一个设置值清除值,在启动微服务之后,这个地方就不是这样写了 export class CacheService { redisClient: Redis; // 先做一个最简易的版本,只生产一个 链接实例 constructor() { this.redisClient = new RedisC({ port: 6379, // Redis port host: '192.168.101.10', // Redis host family: 4, // 4 (IPv4) or 6 (IPv6) password: '', db: 0, }); } // 编写几个设置redis的便捷方法 /** * @Description: 封装设置redis缓存的方法 * @param key {String} key值 * @param value {String} key的值 * @param seconds {Number} 过期时间 * @return: Promise */ public async set(key: string, value: any, seconds?: number): Promise { value = JSON.stringify(value); if (!seconds) { await this.redisClient.set(key, value); } else { await this.redisClient.set(key, value, 'EX', seconds); } } /** * @Description: 设置获取redis缓存中的值 * @param key {String} */ public async get(key: string): Promise { const data = await this.redisClient.get(key); if (data) return data; return null; } /** * @Description: 根据key删除redis缓存数据 * @param key {String} * @return: */ public async del(key: string): Promise { return await this.redisClient.del(key); } /** * @Description: 清空redis的缓存 * @param {type} * @return: */ public async flushall(): Promise { return await this.redisClient.flushall(); } } 有了redis和对他的基础操作之后,我们看看如何实现单点登录

主要的逻辑 是 在签发token的时候把token存到redis中标识key就是用户id,如果下次还有同样的用户id登录就把原来的redis中的key对应的值换成新的,这样先前的那个用户 再次访问的时候发现这个token和之前的不相同,那么就认为它在别的地方登录了,本地就强制下线

image.png

// 在jwt的几个文件中做修改 // /src/moduels/auth/ath.service.ts async certificate(user: User) { const payload = { username: user.username, sub: user.id, }; console.log('JWT验证 - Step 3: 处理 jwt 签证'); try { const token = this.jwtService.sign(payload); // 把token存储到redis中,如果这个用户下次还登录就把这个值更新了,载validate的时候看看能不能 // 找到原来的key的值没有就说明更新了就强制要求用户下线 于是这单点登录功能就完成了 ,过期时间和token一致 await this.CacheService.set( `user-token-${user.id}-${user.username}`, token, 60 * 60 * 8, ); return { code: 200, data: { token, }, msg: `登录成功`, }; } catch (error) { return { code: 600, msg: `账号或密码错误`, }; } } // jwt逻辑 和local逻辑也要变化一下 @Injectable() export class LocalStrategy extends PassportStrategy(Strategy) { constructor(private readonly authService: AuthService) { super(); } async validate(username: string, password: string): Promise { // 本地local的策略于jwt关系不大, console.log('你要调用我哈------------'); const user = await this.authService.validateUser(username, password); if (!user) { throw new UnauthorizedException(); } return user; } } @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor(private readonly CacheService: CacheService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: jwtConstants.secret, passReqToCallback: true, }); } // JWT验证 - Step 4: 被守卫调用 async validate(req: Request, payload: any) { const originToken = ExtractJwt.fromAuthHeaderAsBearerToken()(req); // 只有验证通过之后才会来到这里 // console.log(`JWT验证 - Step 4: 被守卫调用`); const cacheToken = await this.CacheService.get( `user-token-${payload.sub}-${payload.username}`, ); //单点登陆验证 if (cacheToken !== JSON.stringify(originToken)) { throw new UnauthorizedException('您账户已经在另一处登陆,请重新登陆'); } return { username: payload.username, }; } }

于是这样就完成了✅了! 比较简单哈,需要注意的就是你设置的key的在redis中的过期时间,还有就是我们实际上可以优化这个代码 把redis链接抽离出来,传递class 而不是在用的时候采取new ,这个各位大佬,可以自己去琢磨实现一下,也👏 欢迎在评论区 留言分享你的实现,造福一方 哈哈哈😂

如何做微服务?通信架构如何设计?

使用Nestjs我们将会实现一个非常easy的微服务,通信的话可以使用MQ 也可以直接掉用,或者其RPC的方案,但是我没有引入其他负责的东西,只实现了 直接掉用

理论知识 被掉用方需要准备什么?

nest中的微服务需要依赖一个库 @nestjs/microservices,使用它我们可以非常方便的创建微服务应用,现在我们启动另一个项目,(很简单,我们啥也不写就是注册一个服务)注意我们使用最简单的TCP来通信,Nest默认情况下,微服务通过 TCP协议 监听消息。

options的值如下

host连接主机名port连接端口retryAttempts连接尝试的总数retryDelay连接重试延迟(ms) import { NestFactory } from '@nestjs/core'; import { Transport, MicroserviceOptions } from '@nestjs/microservices'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.createMicroservice( AppModule, { transport: Transport.TCP, options: { host: '192.168.101.2', port: 3333, }, }, ); app.listen(); } bootstrap(); // 然后我们去它 controller 写点东西 @Controller() export class AppController { constructor(private readonly appService: AppService) {} @Get() getHello(): string { return this.appService.getHello(); } // 模式2基于 事件 (不一定要求有返回) @EventPattern('math:log') async handleUserCreated(text: Record) { // business logic console.log(text, '基于事件的传输方式'); } // 模式1基于 请求-响应 (要求一来一返) @MessagePattern('math:wordcount') wordCount(text: string): { [key: string]: number } { return this.appService.calculateWordCount(text); } }

什么是模式 模式是微服务之间识别消息的方式,它有下面几种 :1、 请求-响应(默认的方式) 2、基于事件的,上面你所看到的代码中农已经有了详细的说明

掉用方该如何做才能掉用这个服务呢?

// 同样的在module中注入 @Module({ imports: [ ClientsModule.register([ { name: 'NEST_MICRO', transport: Transport.TCP, options: { host: '192.168.101.2', port: 3001, }, }, ]), -----省略部分代码----- // 在你将要掉用它的地方 注入它 constructor( private readonly userService: UserService, @Inject('NEST_MICRO') private client: ClientProxy, ) {} // 启用一个微服务 @Post('/math/wordcount') async wordCount(@Body() { text }: { text: string }) { // 第一种模式 请求响应 const value2 = await this.client.send('math:wordcount', text).toPromise(); // 第二种模式 事件 await this.client.emit('math:log', text).toPromise(); return value2; } 那么我到底适合哪种模式呢?

一般来说 Kafka 或 NATS 更符合事件模式,(既您只想发布事件而不希望等待的时候⌛️)这种情况下使用事件的方式就很好了

实践指南 // 首先在新建一个项目 main中写道 (被掉用方) import { NestFactory } from '@nestjs/core'; import { Transport, MicroserviceOptions } from '@nestjs/microservices'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.createMicroservice( AppModule, { transport: Transport.TCP, options: { host: '192.168.101.2', port: 3333, }, }, ); app.listen(); } bootstrap(); // 在它的controller中写道 (被掉用方) import { Controller, Get } from '@nestjs/common'; import { EventPattern, MessagePattern } from '@nestjs/microservices'; import { AppService } from './app.service'; @Controller() export class AppController { constructor(private readonly appService: AppService) {} @Get() getHello(): string { return this.appService.getHello(); } // 模式2基于 事件 (不一定要求有返回) @EventPattern('math:log') async handleUserCreated(text: Record) { // business logic console.log(text, '基于事件的传输方式'); } // 模式1基于 请求-响应 (要求一来一返) @MessagePattern('math:wordcount') wordCount(text: string): { [key: string]: number } { return this.appService.calculateWordCount(text); } } // 在我们的掉用方的AppModule全局注入 (掉用方) import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { ScheduleModule } from '@nestjs/schedule'; import App_globalConfig from './config/configuration'; import DatabaseConfig from './config/database'; import { AppService } from './app.service'; import { ArticleModule } from './modules/article/article.module'; import { TagModule } from './modules/tag/tag.module'; import { UserModule } from './modules/user/user.module'; import { AuthModule } from './modules/auth/auth.module'; import { FilesModule } from './modules/files/files.module'; import { JobModule } from './modules/job/job.module'; import { CacheModule } from './modules/cache/cache.module'; import { ClientsModule, Transport } from '@nestjs/microservices'; // 注册一个用于对微服务进行数据传输的客户端 @Module({ imports: [ ClientsModule.register([ // 同样的你可以使用registrAsync方式读取config配置 { name: 'NEST_MICRO', transport: Transport.TCP, options: { host: '192.168.101.2', port: 3001, }, }, ]), ScheduleModule.forRoot(), ConfigModule.forRoot({ isGlobal: true, load: [App_globalConfig, DatabaseConfig], }), TypeOrmModule.forRootAsync({ imports: [ConfigModule], useFactory: async (configService: ConfigService) => { return { type: 'mysql', host: configService.get('database.host'), port: Number(DatabaseConfig().port), username: DatabaseConfig().username, password: DatabaseConfig().password, database: DatabaseConfig().database, entities: [__dirname + '/**/*.entity{.ts,.js}'], // 扫描本项目中.entity.ts或者.entity.js的文件 synchronize: true, }; }, inject: [ConfigService], }), UserModule, TagModule, ArticleModule, AuthModule, FilesModule, JobModule, CacheModule, ], providers: [AppService], }) export class AppModule {} // 在userModule进行使用 export class UserController { constructor( private readonly userService: UserService, @Inject('NEST_MICRO') private client: ClientProxy, ) {} // 启用一个人微服务 @Post('/math/wordcount') async wordCount(@Body() { text }: { text: string }) { const value2 = await this.client.send('math:wordcount', text).toPromise(); await this.client.emit('math:log', text).toPromise(); return value2; } Nest到底咋运行的?

如果你拉取我的代码你build之后,然后呢?怎么去部署会运维它呢??🌚🌚 这里我们将会张开讨论到底,嘿嘿这里只是简单的说说,如果大家喜欢看,我后续会完善整个项目

关于build 和运行时

实际上Nest说一个运行时的的框架,它需要和node_module一起start,如果你仅仅是把build的东西就希望它能像前端一样拿出去跑 那就大错特错啦,当然也有不少大神怎么干过 ,它们把nest的在build的时候的webpack配置改了,让它去把这些依赖的库全放到build里面去,其实我觉得都不错。但是这种方式你可能要处理更多的BUG和不确定,因此我还是推荐大家 不要这么干,老实点把node_module拿出去跑就好了

关于运维

其实我希望描述的是nodejs 在业界的部署方式,拿我司举例(Newegg),我们的部署方式是使用pm2去管理它,同 样的基于pm2的功能我们还完成了自动重启等功能。详情可以去看他们的文档

pm2.fenxianglu.cn/docs/advanc…

k8s和Docker

k8s和Docker的部署比较的简单,我举例子docker的部署吧,在我前面的几篇文章中就有说明,如何使用docker去build一个image,这里不重复的讲了,同样的使用之前文档中的gitlab你完全可以实现自己的工作流。

关于nginx的后端反向代理,

实际上这个在单机中做比较的简单,在docker或者k8s中需要倒腾一下,我这里的方案是在宿主机假设nginx然后映射到容器的prot中去 这里就不过多的介绍了,(nginx使用非常的简单)

参考

NestJS官方文档

TypeOrm官方文档

本项目Github地址



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3