神光《Nest 通关秘籍》学习总结 | 您所在的位置:网站首页 › mysql结课心得1000字 › 神光《Nest 通关秘籍》学习总结 |
最近在学习神光大神的《Nest通关秘籍》,该小册主要包含下面这些内容:
接下来的日子里,我将更新一系列的学习笔记。感兴趣的可以关注我的专栏《Nest 通关秘籍》学习总结。 特别申明:本系列文章已经经过作者本人的允许。 大家也不要想着白嫖,我的笔记只是个人边学习边记录的,不是很完整,大家想要深入学习还是要自己去购买原版小册。 本章我们来学习nest中的五花八门的装饰器。 我们在aaa.filter.ts中通过@Catch处理抛出的未捕获异常 import { ArgumentsHost, Catch, ExceptionFilter, HttpException, } from '@nestjs/common'; import { Response } from 'express'; @Catch(HttpException) export class AaaFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { const response: Response = host.switchToHttp().getResponse(); response.status(exception.getStatus()).json({ msg: exception.message, }); } }然后通过 @UseFilters 应用到 handler 上: import { Controller, Get, UseFilters, HttpStatus, HttpException, } from '@nestjs/common'; import { AaaService } from './aaa.service'; import { AaaFilter } from './aaa.filter'; @Controller('aaa') export class AaaController { constructor(private readonly aaaService: AaaService) {} @Get() @UseFilters(AaaFilter) findAll() { throw new HttpException('xxx', HttpStatus.BAD_REQUEST); return this.aaaService.findAll(); } }当访问,可以看到抛出的异常: 除了 filter 之外,interceptor、guard、pipe 也是这样用。 8.请求参数相关的装饰器8.1 @Post post请求可以通过@Body来获取body的部分: @Post() create(@Body() createAaaDto: CreateAaaDto) { console.log(createAaaDto); return this.aaaService.create(createAaaDto); }body参数的结构体一般使用dto来声明,nest 会实例化一个 dto 对象: export class CreateAaaDto { age: number; name: string; }发送一个post请求: 可以看到控制台接收到了一个body参数: 除了 @Post 外,还可以用 @Get、@Put、@Delete、@Patch、@Options、@Head 装饰器分别接受 get、put、delete、patch、options、head 请求。 9.@SetMetadata指定metadata // aaa.controller.ts import { Controller, Get, SetMetadata, UseGuards, } from '@nestjs/common'; import { AaaService } from './aaa.service'; import { CreateAaaDto } from './dto/create-aaa.dto'; import { UpdateAaaDto } from './dto/update-aaa.dto'; import { AaaGuard } from './aaa.guid'; @Controller('aaa') @SetMetadata('roles', ['user']) export class AaaController { constructor(private readonly aaaService: AaaService) {} @Get() @UseGuards(AaaGuard) @SetMetadata('roles', ['admin']) findAll() { return this.aaaService.findAll(); } } // aaa.guard.ts import { CanActivate, ExecutionContext, Inject, Injectable, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { Observable } from 'rxjs'; @Injectable() export class AaaGuard implements CanActivate { @Inject(Reflector) private readonly reflector: Reflector; canActivate( context: ExecutionContext, ): boolean | Promise | Observable { const classMetaDatas = this.reflector.get('roles', context.getClass()); const methodMetaDatas = this.reflector.get('roles', context.getHandler()); console.log(classMetaDatas, methodMetaDatas); return true; } }要使用 session 需要安装一个 express 中间件: pnpm install express-session在 main.ts 里引入并启用: import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import * as session from 'express-session'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use( session({ secret: 'xiumu', cookie: { maxAge: 1000 }, }), ); await app.listen(3000); } bootstrap();发送一个请求 会返回 Set-Cookie 的响应头,设置了 cookie,包含 sid 也就是 sesssionid。 12.@HostParam获取域名部分的参数 import { Controller, Get, HostParam, } from '@nestjs/common'; import { BbbService } from './bbb.service'; @Controller({ host: ':host.0.0.1', path: 'bbb' }) export class BbbController { constructor( private readonly bbbService: BbbService, ) {} @Get('host') host(@HostParam('host') host) { return host; } }这样只有通过xxx.0.0.1访问的才能通过。 13.@Req获取请求参数 @Get('req') getBbbb(@Req() req: Request) { console.log('req', req.hostname, req.url); }之前我们用@SetMetadata设置了一些角色数据,然后通过Guard再判断逻辑。现在我们把@SetMetaData来自定义一下: // bbb.decorator.ts import { SetMetadata } from '@nestjs/common'; export const Bbb = (...args: string[]) => SetMetadata('aaa', args);然后在Controller中使用: import { Controller, Get, HostParam, UseGuards, } from '@nestjs/common'; import { Request } from 'express'; import { BbbService } from './bbb.service'; import { Bbb } from './bbb.decorator'; import { BbbGuard } from './bbb.guard'; @Controller({ host: ':host.0.0.1', path: 'bbb' }) export class BbbController { constructor( private readonly bbbService: BbbService, ) {} @Get() @UseGuards(BbbGuard) @Bbb('admin') findAll() { return this.aaaService.findAll(); } }这样我们可以使用上面的方式来使用这个装饰器。测试一下: 现在我们这里的装饰器有点多: 我们可以把这三个装饰合并成一个使用: // merge.decorator.ts import { applyDecorators, Get, UseGuards } from '@nestjs/common'; import { Bbb } from './bbb.decorator'; import { BbbGuard } from './bbb.guard'; export function Merge(path, role) { return applyDecorators(Get(path), Bbb(role), UseGuards(BbbGuard)); }使用的时候可以这样使用: import { Controller, Get, HostParam, Req, UseGuards, } from '@nestjs/common'; import { Request } from 'express'; import { BbbService } from './bbb.service'; import { AaaService } from 'src/aaa/aaa.service'; import { Bbb } from './bbb.decorator'; import { BbbGuard } from './bbb.guard'; import { Merge } from './merge.decorator'; @Controller({ host: ':host.0.0.1', path: 'bbb' }) export class BbbController { constructor( private readonly bbbService: BbbService, private readonly aaaService: AaaService, ) {} @Get() @UseGuards(BbbGuard) @Bbb('admin') findAll() { return this.aaaService.findAll(); } @Merge('hello2', 'admin') getHello3(): string { return 'this is merge decorator'; } }访问http://127.0.0.1:3000/bbb/hello2: 效果和之前一样。 15.自定义参数装饰器先来写一个参数装饰器: // ccc.decorator.ts import { createParamDecorator, ExecutionContext } from '@nestjs/common'; export const Ccc = createParamDecorator( (data: string, ctx: ExecutionContext) => { return 'ccc'; }, );这样使用: import { Controller, Get } from '@nestjs/common'; import { CccService } from './ccc.service'; import { Ccc } from './ccc.decorator'; @Controller('ccc') export class CccController { constructor(private readonly cccService: CccService) {} @Get('arg') getArg(@Ccc() c) { return c; } }这里拿到的c就是参数装饰器的返回值。 在我们自定义的装饰器中,data 很明显就是传入的参数,而 ExecutionContext 前面用过,可以取出 request、response 对象。 这样,那些内置的 @Param、@Query、@Ip、@Headers 等装饰器,我们也可以自己实现了。 下面我们实现一个Header装饰器: import { createParamDecorator, ExecutionContext } from '@nestjs/common'; import { Request } from 'express'; export const MyHeaders = createParamDecorator( (key: string, ctx: ExecutionContext) => { const request: Request = ctx.switchToHttp().getRequest(); return key ? request.headers[key] : request.headers; }, ); @Get('my') getHeader(@Headers('Accept') headers1, @MyHeaders('accept') headers2) { console.log('headers1', headers1); console.log('headers2', headers2); }这里我们的@@MyHeaders和@Headers具有同样的效果,都能拿到Accept的值。 注意:@MyHeaders()接受的参数是accept,小写开头。 下面我们再实现一个query装饰器。 export const MyQuery = createParamDecorator( (key: string, ctx: ExecutionContext) => { const request: Request = ctx.switchToHttp().getRequest(); return request.query[key]; }, ); @Get('query') getHello6(@Query('aaa') aaa, @MyQuery('bbb') bbb) { console.log('aaa', aaa); console.log('bbb', bbb); }我们通过断点调试的方式,来看看: 可以看到在request中,可以拿到query返回的参数,我们根据key值,取到返回了。 16.自定义class装饰器同样的,class的装饰器也可以自定义: // myHeaders.decorator.ts export const MyClass = () => Controller('class'); import { Controller, Get, Headers, Query } from '@nestjs/common'; import { CccService } from './ccc.service'; import { Ccc } from './ccc.decorator'; import { MyHeaders, MyQuery, MyClass } from './myHeaders.decorator'; // @Controller('ccc') @MyClass() export class CccController { constructor(private readonly cccService: CccService) {} @Get('arg') getArg(@Ccc() c) { return c; } @Get('my') getHeader(@Headers('Accept') headers1, @MyHeaders('Accept') headers2) { console.log('headers1', headers1); console.log('headers2', headers2); } @Get('query') getHello6(@Query('aaa') aaa, @MyQuery('bbb') bbb) { console.log('aaa', aaa); console.log('bbb', bbb); } }我们把@Controller('ccc')替换成了@MyClass(),然后访问一下http://127.0.0.1:3000/class/query?aaa=aaa&bbb=bbb,同样也是没问题的。 总结: 内置装饰器不够用的时候,或者想把多个装饰器合并成一个的时候,都可以自定义装饰器。 方法的装饰器就是传入参数,调用下别的装饰器就好了,比如对 @SetMetadata 的封装。 如果组合多个方法装饰器,可以使用 applyDecorators api。 还可以通过 createParamDecorator 来创建参数装饰器,它能拿到 ExecutionContext,进而拿到 reqeust、response,可以实现很多内置装饰器的功能,比如 @Query、@Headers 等装饰器。 |
CopyRight 2018-2019 实验室设备网 版权所有 |