当前位置: 首页 > Web前端 > HTML

从Express到Nestjs,说说Nestjs的设计思路和使用方法

时间:2023-04-02 14:10:46 HTML

最近一段时间一直在用nestjs,写起来让人有种javaspring的感觉。Nestjs可以使用express所有的中间件,而且它还有完善的支持Typescript,配合数据库关系映射typeorm使用,可以快速写出一个接口网关。本文将介绍企业级节点框架的特点和优势。从DependencyInjection(DI)谈装饰器和注解nestjs的“洋葱模型”nestjs的特点总结在我的博客:https://github.com/forthealll...欢迎star和fork1.从dependencyinjection(DI)说到(1),Angular中的依赖注入从angular1.x开始,已经实现了依赖注入或控制反转的模式。在angular1.x中,有控制器(controller)、服务(service)、模块(module)。作者早年写过一段时间的angular1.3。下面是一个例子来说明:varmyapp=angular.module('myapp',['ui.router']);myapp.controller('test1',function($scope,$timeout){}myapp.controller('test2',function($scope,$state){}上面是angular1.3中依赖注入的一个例子,首先定义了一个名为“myapp”的模块。然后在myapp模块中定义controller控制器。myapp模块交给myapp.controller函数,具体的依赖注入流程图如下:myapp模块如何定义由它的两个controller决定,另外还依赖$scope、$timeout等服务在controller中。这样就实现了依赖注入,或者说控制反转。(2)什么是依赖注入用一个例子来解释什么是依赖注入。猫类(){}老虎类(){}动物园类(){构造函数(){this.tiger=newTiger();this.cat=newCat();}}‖在上面的例子中,我们在其构造方法中定义了Zoo、InstantiateCat和Tiger。这时候如果我们想给Zoo添加一个实例变量,比如修改Zoo类本身,比如我们现在想给Zoo类添加一个Fish类的实例变量:classFish(){}classZoo(){constructor(){this.tiger=newTiger();this.cat=newCat();this.fish=newFish();}}另外如果我们要修改Zoo实例化时,传递给Tiger和Cat类的变量也必须在Zoo类上进行修改。这种反复修改会使Zoo类不具有通用性,从而需要反复测试Zoo类的功能。我们想象将实例化过程作为参数传递给Zoo类:classZoo(){constructor(options){this.options=options;}}varzoo=newZoo({tiger:newTiger(),cat:newCat(),fish:newFish()})‖‖我们将实现过程放入参数中,传递给Zoo的构造函数,这样我们不必在Zoo类中反复修改代码。这是一个引入依赖注入的简单例子。更完整的使用依赖注入可以给Zoo类添加静态方法和静态属性:classZoo(){staticanimals=[];构造函数(选项){this.options=options;这个。初始化();}init(){let_this=this;animals.forEach(function(item){item.call(_this,options);})}staticuse(module){animals.push([...module])}}Zoo.use[Cat,Tiger,Fish];varzoo=newZoo(选项);上面提到,我们使用Zoo的静态方法use将Cat、Tiger、Fish模块注入到Zoo类中,Zoo的具体实现交给Cat、Tiger、Fish模块,以及传入的options参数构造函数。(3)nestjs中的依赖注入angular中依赖注入的思想在nestjs中也有参考,同样使用了module、controller和service。@Module({imports:[otherModule],providers:[SaveService],controllers:[SaveController,SaveExtroController]})exportclassSaveModule{}上面是nestjs中定义一个模块的方法,可以在imports属性中注入其他模块,并在providers控制器中注入需要使用的相应服务,在控制器中注入需要的控制器。2.装饰器和注解在nestjs中,typescript被完美拥抱,尤其是大量使用了装饰器和注解。装饰器和注解的理解可以参考我的文章:Typescript中的装饰器和注解。看看使用装饰器和注解后在nestjs中写业务代码是多么简单:)findAll(@Req()req,@Res()res){return'这个动作返回所有的猫';}}上面定义了两个处理url为“/cats”的控制器,为这个路由的get方法定义了findAll函数。使用get方法请求/cats时,会主动触发findAll函数。另外,在findAll函数中,通过req和res参数,也可以直接在主题中使用request和request的response。比如我们通过req获取请求的参数,通过res.send返回请求结果。3、nestjs的“洋葱模型”这里简单说一下nestjs中如何分层,也就是说请求到达服务器后如何一层一层的处理,直到请求得到响应并将结果返回给客户端。在nestjs中,在service的基础上,根据处理级别增加了中间件、异常过滤器、管道、守卫、拦截器。处理函数之间存在多层处理。上图中的逻辑就是分层处理的过程。经过分层处理的请求可以到达服务器处理函数。下面介绍一下nestjs中分层模型的具体作用。(1)、中间件nestjs中的middle和express中间件完全一样。不仅如此,我们还可以直接使用express中的中间件。例如,在我的应用程序中,我需要处理核心跨域:import*ascorsfrom'cors';asyncfunctionbootstrap(){onstapp=awaitNestFactory.create(/*创建应用的业务逻辑*/)app.use(cors({origin:'http://localhost:8080',credentials:true}));等待app.listen(3000)}bootstrap();在上面的代码中,我们可以直接通过app.use来使用coreexpress中的中间件。使服务器端支持核心跨域等。除此之外,带有nestjs的中间件也完全保留了express中中间件的特点:中间件接受response和request作为参数,可以修改请求对象request和结果返回对象response来结束对request的处理,直接返回请求的结果,也就是说可以直接在中间件中res.send等,中间件处理完后,如果没有返回请求结果,可以将中间件传递给下一个中间件处理通过下一个方法。在nestjs中,中间件和express中完全一样。除了复用express中间件,在nestjs中针对特定路由使用中间件也很方便:}}以上是在具体路由url为/cats时使用LoggerMiddleware中间件。(2)、异常过滤器异常过滤器可以捕获后端验收过程中任何阶段发生的异常,捕获异常后,将处理后的异常结果返回给客户端(如返回错误码、错误信息等)。我们可以自定义一个异常过滤器,在这个异常过滤器中,我们可以指定需要捕获哪些异常,以及这些异常应该返回什么结果等。例如,自定义过滤器用于捕获HttpException异常的例子。@Catch(HttpException)exportclassHttpExceptionFilterimplementsExceptionFilter{catch(exception:HttpException,host:ArgumentsHost){constctx=host.switchToHttp();constresponse=ctx.getResponse();constrequest=ctx.getRequest()status=exception.getStatus();response.status(status).json({statusCode:status,timestamp:newDate().toISOString(),path:request.url,});}}我们可以看到host是ArgumentsHost接口,如果实现了ArgumentsHost接口,在host中可以获取到运行环境中的信息,在http请求中可以获取request和response,还可以获取client和data信息在套接字中获得。同样,对于异常过滤器,我们可以指定在某个模块中使用,也可以指定全局使用。(3)Pipes??Pipes一般用户验证请求中的参数是否符合要求,起到验证参数的作用。例如,我们需要验证或转换请求中某些参数的类型:@Injectable()exportclassParseIntPipeimplementsPipeTransform{transform(value:string,metadata:ArgumentMetadata):number{constval=parseInt(价值,10);if(isNaN(val)){thrownewBadRequestException('验证失败');}返回值;}}?上面的ParseIntPipe可以将参数转换为十进制整数。我们可以这样使用:@Get(':id')asyncfindOne(@Param('id',newParseIntPipe())id){returnawaitthis.catsService.findOne(id);}为参数id中get请求,调用新的ParseIntPipe方法将id参数转换为十进制整数。(4)GuardsGuards守卫,它的作用是判断一个请求是否应该被处理函数接受和处理,当然我们也可以在middleware中间件中处理接受请求,相对于中间件,Guards可以得到更多有关请求执行上下文的详细信息。通常Guards守卫层位于中间件之后,请求被处理函数正式处理之前。下面是一个Guards的例子:@Injectable()exportclassAuthGuardimplementsCanActivate{canActivate(context:ExecutionContext,):boolean|承诺<布尔值>|Observable{constrequest=context.switchToHttp().getRequest();返回验证请求(请求);}}这里的context实现了一个ExecutionContext接口,里面有丰富的执行上下文信息。导出接口ArgumentsHost{getArgs=any[]>():T;getArgByIndex(index:number):T;switchToRpc():RpcArgumentsHost;switchToHttp():HttpArgumentsHost;switchToWs():WsArgumentsHost;}exportinterfaceExecutionContextextendsArgumentsHost{getClass():Type;getHandler():Function;}ExecutionContext除了ArgumentsHost中的信息外,还包括getClass用户获取某条处理过的路由,controller。而getClass用于获取并返回指定路由后台处理的处理函数。对于Guardshandler,如果返回true,请求将被正常处理,如果返回false,请求将抛出异常。(5)、拦截器Interceptors拦截器可以绑定到每一个需要执行的函数上,拦截器会在函数执行之前或之后运行。您可以转换函数执行后返回的结果等。简而言之:拦截器可以在函数执行之前或之后运行。如果在执行之后运行,可以拦截函数执行的返回结果,修改参数等。再举一个超时处理的例子:@Injectable()exportclassTimeoutInterceptorimplementsNestInterceptor{intercept(context:ExecutionContext,call$:Observable<任何>):Observable<任何>{returncall$.pipe(timeout(5000));}}这个拦截器可以定义在控制器上,可以处理超时请求。4.nestjs的特点总结??最后总结一下nestjs的优缺点。nestjs的优点:完美支持typescript,可以使用日渐繁荣的ts生态工具兼容express中间件,因为express是最早的轻量级node服务端框架,nestjs可以使用所有express中间件,使其成为生态的完美层-逐层处理可以在一定程度上约束代码,比如什么时候使用中间件,什么时候使用守卫等。依赖注入和模块化的思想提供了完整的MVC链接,使得代码结构清晰易行维护。这里的m表示数据层可以以模块的形式注入。例如,可以通过typeorm的实体将模块注入到模块中。完美支持rxjs