nest.js学习笔记(一)


1、前期准备工作

环境配置参考node里的nodemon或者webpack

在学习nest.js前需要了解它的反映机制 Reflect.defineMetadata, 学习这个需要安装和引入库‘reflect-metadata’这个库

npm i reflect-metadata --save
import 'reflect-metadata'

console.log(Reflect.defineMetadata); //可以打印出对应的方法说明安装成功

Reflect-Metadata的方法介绍

import 'reflect-metadata' let Info = { name: 'aaa', getName() { return this.name }, } //定义在对象上的元数据
Reflect.defineMetadata('info', '这是个对象元数据信息', Info) let msg = Reflect.getMetadata('info', Info) console.log(msg) //定义在对象属性上的元数据
Reflect.defineMetadata('paramInfo', '这是个对象属性元数据', Info, 'name') let paramMsg = Reflect.getMetadata('paramInfo', Info, 'name') console.log(paramMsg) //判断元数据上是否有指定的数据
console.log(Reflect.hasMetadata('info', Info)) // true
console.log(Reflect.hasMetadata('paramInfo', Info, 'getName')) // false

//如果子类继承了父类原型链上的方法,那么同时也会继承原型链上的metadata,这个时候就可以使用hasOwnMetadata来判断是否是自有的属性
console.log(Reflect.hasOwnMetadata('info', Info))

注意:使用defineMetadata不会影响原数据本身,只会增加元数据

Reflect中类中的使用

import 'reflect-metadata' @Reflect.metadata('name', 'metaData_className') //这个是直接定义,相当于defineMetadata
class Test { @Reflect.metadata('name', 'metaData_className_state') public state?: string @Reflect.metadata('name', 'metaData_className_method') public check(param: string): void {} } console.log(Reflect.getMetadata('name', Test)) console.log(Reflect.getMetadata('name', Test.prototype, 'state')) console.log(Reflect.getMetadata('name', Test.prototype, 'check')) //获取所有的原型上的key值
console.log(Reflect.getMetadataKeys(Test.prototype)) //获取原型上指定属性的key值
console.log(Reflect.getMetadataKeys(Test.prototype, 'state')) // 注意: 特殊类型 // design:type 表示属性的声明的类型 // design:paramtypes 表示函数参数的声明类型,返回的是一个数组 // design:returntype 表示函数的返回类型

注意:Reflect.metadata如何修复的是属性或者类里的方法,那么相当对应的属性值是挂载在prototype原型上

可以通过自己写装饰器,将这个东西封装入自己封装的装饰器中

import 'reflect-metadata' const classMetadata = <T>(name: string, value: string|number|boolean) => { return (classT: T) => { Reflect.defineMetadata(name, value, classT) } } const methodMetadata = <T>(name: string, value: string|number|boolean) => { return (classT: T, methodT: string|symbol) => { Reflect.defineMetadata(name, value, classT, methodT) } }

@classMetadata('name', 'ven')
class Test {

    @methodMetadata('name', 'state')
    public state: string

    @methodMetadata('name', 'method')
    public check(param: string): void {
    }
}

console.log(Reflect.getMetadata('name', Test)) console.log(Reflect.getMetadata('name', Test.prototype, 'state')) console.log(Reflect.getMetadata('name', Test.prototype, 'check'))

依赖注入小试

import 'reflect-metadata' class Logger { public constructor() { console.log('this is logger') } public getLog() {} } // 使用依赖注入,实例化声明属性的类 function Inject(injectId: string): PropertyDecorator { return function (targetClassPrototype, propName) { let PropClass = Reflect.getMetadata('design:type', targetClassPrototype, propName) let PropClassObj = new PropClass() console.log(PropClassObj) } } class Active { @Inject('Logger') public log?: Logger public getPeo() { return this.log } } //使用依赖注册实例化构造器参数中声明的类 function InjectContructor(injectId?: string): ParameterDecorator { return function (targetClassPrototype, propertyKey, parameterIndex) { let propClass = Reflect.getMetadata('design:paramtypes', targetClassPrototype) let loggerClass = new propClass[parameterIndex]() } } class Person { public constructor(@InjectContructor('Logger') private logger: Logger, private count: number) {} }

注意:以上方法的使用需要建立在tsconfig.json中target:es5的基础上,否则获取不到相关的值

2、nest框架的安装

$ npm i -g @nestjs/cli
$ nest new project-name

3、nest结构介绍

 

在nest.js中路径是指向根目录下,如 src/controllers表示的是根目录下的controllers文件夹

4、controller的使用

 controller的创建,可以使用命令:

nest g controller 模块名称

就会独立生成指定文件的模块,里面有 模块名称.controller.ts   模块名称.controller.spec.ts

controller的入门案例

import { Controller, Get } from '@nestjs/common'; import { AppService } from './app.service'; @Controller() //前缀是/
export class AppController { // 只需要在构造函数里声明依赖,IOC窗容器会自动帮你注入实例,你直接调用就可以了
 constructor(private readonly appService: AppService) {} @Get() //表示的是/的响应, 控制器里面一般只用于接收参数,返回响应,并不会真正处理业务
 getHello(): string { return this.appService.getHello(); } @Get('api')  // 访问的/api的路径进行响应的
  getApi(): Array<string> { return ['aa', 'bbb', 'cc'] } }

注意:在使用controller的时候 @controller('prefix')是表示一个控制器的修饰符,prefix表示访问路径的前缀,如@controller('user'), 那么@get('/api')时则表示访问/user/api路径时的响应, 这里的@get表示发送请求的类型是get

 controller方法的响应方式

通常来讲,controller里的响应会自动返回200的状态码,通常来讲,有两种响应方式

 注意:通常来讲安装完整项目,内部已经安装了@types/express这个模块了

import { Controller, Get, Req, Res, Query, Ip, HttpStatus, Param, Header, Post, Body, UploadedFile } from '@nestjs/common'; import { AppService } from './app.service'; import { Request, Response } from 'express' @Controller('user') //前缀是/user
export class AppController { // 只需要在构造函数里声明依赖,IOC窗容器会自动帮你注入实例,你直接调用就可以了
 constructor(private readonly appService: AppService) {} @Get() //表示的是/user的响应, 控制器里面一般只用于接收参数,返回响应,并不会真正处理业务
 getHello(): string { return this.appService.getHello(); } @Get('api')  // 访问的/user/api的路径进行响应的
  getApi(@Req() request: Request, @Res() response: Response, @Query('age') query: string): any { response.status(HttpStatus.OK).json({ ...request.query, query }) } @Get('ip')  // 获取ip
 getIp(@Req() request: Request, @Ip() ip ) { return [request.ip, ip] } @Get('header') // 获取host相关的信息
  @Header('token', '123') getHeader(@Req() request: Request) { return [request.headers, request.header('token')] // 这个是一个对象,如果是request,header则是一个方法
 } @Get('param/:id') // 或者使用:id
  getParams(@Param('id') id: string) { return id; } @Post('param') getPost(@Req() request: Request, @Body() body, @UploadedFile() files) { return [request.method, request.body, body, files] } }

注意:如果引入了@Res() 那么return 将不起作用,需要用response.json或response.send进行返回,但是对于模板的渲染还是按原有方法操作

注意:路由跳转在response里面,  response.redirect('/test')表示跳转到路由下 

5、配置静态资源

文档地址: https://docs.nestjs.com/techniques/mvc

import { NestFactory } from '@nestjs/core'; import { NestExpressApplication } from '@nestjs/platform-express'; import { AppModule } from './app.module'; import {join} from 'path' async function bootstrap() { const app = await NestFactory.create<NestExpressApplication>(AppModule); //写法一 // app.useStaticAssets('public') //没有配置虚拟路径 访问目录 http://localhost:3000/资源名称 //写法二 app.useStaticAssets('public', { //设置虚拟路径 那么访问的时候就需要用 http://localhost:3000/static/资源名称 prefix: '/static/' }) //写法三 // app.useStaticAssets(join(__dirname, '..', 'public'),{ // 注意该写法与上面写法是一样的效果 // prefix: '/static/',  // });
 await app.listen(3000); } bootstrap();

注意:以上是main.ts入口文件,注意__dirname是指在项目下的dist文件,所以在指定路径的时候,需要注意指向

 6、配置模板引擎

在nest.js中使用的是hbs模板引擎,但是这边使用的是ejs那么需要配置这个模板引擎

npm i ejs --save

把模板引擎配置到入口文件中

app.setBaseViewsDir(join(__dirname, '..', 'views')) // 放视图的文件
app.setViewEngine('ejs'); // 指定模板引擎

注意:上述代码指定了视图的根目录为views文件夹下,引擎是ejs, 这就需要在根目录下创建views,渲染的时候会自动读取views文件夹下的文件

import { Controller, Get, Render } from '@nestjs/common'; import { AppService } from './app.service'; @Controller() export class AppController { ... @Get('test') @Render('test/index') //指定的是views下test文件下的index.ejs文件
  public testInfo(): {[key: string]: string|number} { return {name: 'ven', age: 20, img: '/static/6.jpg'} //返回需要渲染的数据
 } ... }

具体的ejs的使用可以参看ejs的官方文档

7、nest.js中的服务

Nestjs 中的服务可以是 service 也可以是 provider。他们都可以通过 constructor 注 入依赖关系。服务本质上就是通过@Injectable() 装饰器注解的类。在 Nestjs 中服务相 当于 MVC 的 Model。 

服务的创建

nest g service 服务名 (推荐) nest g provider 服务名 帮助信息 nest g --help 
import { Injectable } from '@nestjs/common'; @Injectable() export class YfService { getAll() { return ['aa', 'bb', 'cc'] } }

注意:系统自动生成服务后,会在app.module.ts中的providers中自动引入 ,如果没有自动引用,则需要手动引入

import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { YfController } from './yf/yf.controller'; import { AppService } from './app.service'; import { YfService } from './yf/yf.service'; @Module({ imports: [], controllers: [AppController, YfController], providers: [AppService, YfService], }) export class AppModule {}

那么如果在需要在controller中使用,也需要进行声明

import { Controller, Get } from '@nestjs/common'; import { YfService } from './yf.service'; @Controller('yf') export class YfController {  public constructor(private yfservice: YfService) {} @Get() public index() { return this.yfservice.getAll() } }

8、nest中cookie的使用

NestJs 中使用 Cookie 的话我们可以用 cookie-parser来实现 文档位置: https://docs.nestjs.com/techniques/cookies

安装

npm i cookie-parser npm i -D @types/cookie-parser

引入到main.ts中

import * as cookieParser from 'cookie-parser';
app.use(cookieParser('加密字符串')); //如果需要加密,则需要配置加密字符串,建议配置,配置后可使用加密状态也可以不使用加密

controller中的使用

import { Controller, Get, Req, Res } from '@nestjs/common';
import { YfService } from './yf.service';
import { Response, Request } from 'express';

@Controller('yf')
export class YfController {
    public constructor(private yfservice: YfService) {}

    @Get()
    public index(@Res() res: Response) {
        // res.cookie('name', 'this is test name', {maxAge: 90000, httpOnly: true}) =》不加密 //这里设置了过期时间,以及是否只有http访问 res.cookie('name', 'this is test name', {maxAge: 90000, httpOnly: true, signed: true}) =》 加密
        res.send('this is test')
    }

    @Get('cookie')
    public getCookie(@Req() req: Request): string {
        // return req.cookies.name =》 不加密访问 // 加密后的cookie访问 return req.signedCookies.name =》 加密访问
    }
}

cookie-parser的参数说明

 

domain: 域名

expires : 过 期 时 间 ( 秒 ) , 在 设 置 的 某 个 时 间 点 后 该 Cookie 就 会 失 效 , 如 expires=Wednesday, 09-Nov-99 23:12:40 GMT maxAge: 最大失效时间(毫秒),设置在多少后失效

secure: 当 secure 值为 true 时,cookie 在 HTTP 中是无效,在 HTTPS 中才有效

path: 表示 cookie 影响到的路,如 path=/。如果路径不能匹配时,浏览器则不发送这 个 Cookie

httpOnly:是微软对 COOKIE 做的扩展。如果在 COOKIE 中设置了“httpOnly”属性,则通 过程序(JS 脚本、applet 等)将无法读取到 COOKIE 信息,防止 XSS 攻击产生

signed : 表 示 是 否 签 名 cookie, 设 为 true 会 对 这 个 cookie 签 名 , 这 样 就 需 要 用 res.signedCookies 而不是 res.cookies 访问它。被篡改的签名 cookie 会被服务器拒绝,并且 cookie 值会重置为它的原始值

设置cookie

res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) res.cookie('name', 'tobi', { domain: '.example.com', path: '/admin', secure: true }); res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly:true })

获取cookie

req.cookies.name

删除cookie

res.cookie('rememberme', '', { expires: new Date(0)}); res.cookie('username','zhangsan',{domain:'.ccc.com',maxAge:0,httpOnly:true});

9、nest.js debugger调试

https://github.com/nestjs/docs.nestjs.com/issues/217

打开界面

在.vscode下的launch.json里配置如下:

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "attach",
      "name": "Attach NestJS WS",
      "port": 9229,
      "restart": true,
      "stopOnEntry": false,
      "protocol": "inspector"
    }
  ]
}

在项目路径下支行npm run start:debug

在vscode中启动调试界面

 

 这个时候调用chrome里的inspect监听9229端口

 

 当代码运行那指定的debugger的地点时就会启用断点,打印指定的变量了

同时也可以点击下面的界面进行调试

 10、自定义装饰器

以设置cookie为例封装一个获取cookie的装饰器

import { createParamDecorator, ExecutionContext } from "@nestjs/common"; import { Request } from "express"; export const Cookies = createParamDecorator((data: string, ctx: ExecutionContext) => { // ctx可以获取到上下文的request和response const request: Request = ctx.switchToHttp().getRequest(); return data? request.signedCookies?.[data]: request.signedCookies; })

这样就可以在controller中进行使用了

import { Controller, Get, Res  } from '@nestjs/common';
import { Response } from 'express';
import { Cookies } from 'src/utils/decorators';
import { YfService } from './yf.service';

@Controller('yf')
export class YfController {
    public constructor(private yfservice: YfService) {}

    @Get()
    public index(@Res() res: Response) {
        res.cookie('name', 'this is test name', {maxAge: 90000, httpOnly: true, signed: true})
        res.send('this is test')
    }

    @Get('cookie')
    public getCookie(@Cookies('name') data): string {
        return data;
    }
}

 装饰器嵌套

import { applyDecorators } from '@nestjs/common'; export function Auth(...roles: Role[]) { return applyDecorators( SetMetadata('roles', roles), UseGuards(AuthGuard, RolesGuard), ApiBearerAuth(), ApiUnauthorizedResponse({ description: 'Unauthorized' }), ); }

 使用

@Get('users') @Auth('admin') findAllUsers() {}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM