摘要:三的洋蔥模型這里簡單講講在中是如何分層的,也就是說請求到達服務端后如何層層處理,直到響應請求并將結果返回客戶端。從而使得端支持跨域等。
??最近已經使用過一段時間的nestjs,讓人寫著有一種java spring的感覺,nestjs可以使用express的所有中間件,此外完美的支持typescript,與數據庫關系映射typeorm配合使用可以快速的編寫一個接口網關。本文會介紹一下作為一款企業級的node框架的特點和優點。
從依賴注入(DI)談起
裝飾器和注解
nestjs的“洋蔥模型”
nestjs的特點總結
原文在我的博客中: https://github.com/forthealll...
歡迎star和fork
一、從依賴注入(DI)談起 (1)、angular中的依賴注入??從angular1.x開始,實現了依賴注入或者說控制反轉的模式,angular1.x中就有controller(控制器)、service(服務),模塊(module)。筆者在早年間寫過一段時間的angular1.3,下面舉例來說明:
var myapp=angular.module("myapp",["ui.router"]); myapp.controller("test1",function($scope,$timeout){} myapp.controller("test2",function($scope,$state){}
??上面這個就是angular1.3中的一個依賴注入的例子,首先定義了模塊名為“myapp”的module, 接著在myapp這個模塊中定義controller控制器。將myapp模塊的控制權交給了myapp.controller函數。具體的依賴注入的流程圖如下所示:
myapp這個模塊如何定義,由于它的兩個控制器決定,此外在控制器中又依賴于$scope、$timeout等服務。這樣就實現了依賴注入,或者說控制反轉。
(2)、什么是依賴注入??用一個例子來通俗的講講什么是依賴注入。
class Cat(){ } class Tiger(){ } class Zoo(){ constructor(){ this.tiger = new Tiger(); this.cat = new Cat(); } }
??上述的例子中,我們定義Zoo,在其constructor的方法中進行對于Cat和Tiger的實例化,此時如果我們要為Zoo增加一個實例變量,比如去修改Zoo類本身,比如我們現在想為Zoo類增加一個Fish類的實例變量:
class Fish(){} class Zoo(){ constructor(){ this.tiger = new Tiger(); this.cat = new Cat(); this.fish = new Fish(); } }
??此外如果我們要修改在Zoo中實例化時,傳入Tiger和Cat類的變量,也必須在Zoo類上修改。這種反反復復的修改會使得Zoo類并沒有通用性,使得Zoo類的功能需要反復測試。
我們設想將實例化的過程以參數的形式傳遞給Zoo類:
class Zoo(){ constructor(options){ this.options = options; } } var zoo = new Zoo({ tiger: new Tiger(), cat: new Cat(), fish: new Fish() })
??我們將實力化的過程放入參數中,傳入給Zoo的構造函數,這樣我們就不用在Zoo類中反復的去修改代碼。這是一個簡單的介紹依賴注入的例子,更為完全使用依賴注入的可以為Zoo類增加靜態方法和靜態屬性:
class Zoo(){ static animals = []; constructor(options){ this.options = options; this.init(); } init(){ let _this = this; animals.forEach(function(item){ item.call(_this,options); }) } static use(module){ animals.push([...module]) } } Zoo.use[Cat,Tiger,Fish]; var zoo = new Zoo(options);
??上述我們用Zoo的靜態方法use往Zoo類中注入Cat、Tiger、Fish模塊,將Zoo的具體實現移交給了Cat和Tiger和Fish模塊,以及構造函數中傳入的options參數。
(3)、nestjs中的依賴注入??在nestjs中也參考了angular中的依賴注入的思想,也是用module、controller和service。
@Module({ imports:[otherModule], providers:[SaveService], controllers:[SaveController,SaveExtroController] }) export class SaveModule {}
??上面就是nestjs中如何定一個module,在imports屬性中可以注入其他模塊,在prividers注入相應的在控制器中需要用到的service,在控制器中注入需要的controller。
二、裝飾器和注解??在nestjs中,完美的擁抱了typescript,特別是大量的使用裝飾器和注解,對于裝飾器和注解的理解可以參考我的這篇文章:Typescript中的裝飾器和注解。我們來看使用了裝飾器和注解后,在nestjs中編寫業務代碼有多么的簡潔:
import { Controller, Get, Req, Res } from "@nestjs/common"; @Controller("cats") export class CatsController { @Get() findAll(@Req() req,@Res() res) { return "This action returns all cats"; } }
??上述定義兩個一個處理url為“/cats”的控制器,對于這個路由的get方法,定義了findAll函數。當以get方法,請求/cats的時候,就會主動的觸發findAll函數。
??此外在findAll函數中,通過req和res參數,在主題內也可以直接使用請求request以及對于請求的響應response。比如我們通過req上來獲取請求的參數,以及通過res.send來返回請求結果。
三、nestjs的“洋蔥模型”??這里簡單講講在nestjs中是如何分層的,也就是說請求到達服務端后如何層層處理,直到響應請求并將結果返回客戶端。
在nestjs中在service的基礎上,按處理的層次補充了中間件(middleware)、異常處理(Exception filters)、管道(Pipes),守衛(Guards),以及攔截器(interceptors)在請求到打真正的處理函數之間進行了層層的處理。
上圖中的邏輯就是分層處理的過程,經過分層的處理請求才能到達服務端處理函數,下面我們來介紹nestjs中的層層模型的具體作用。
(1)、middleware中間件??在nestjs中的middle完全跟express的中間件一摸一樣。不僅如此,我們還可以直接使用express中的中間件,比如在我的應用中需要處理core跨域:
import * as cors from "cors"; async function bootstrap() { onst app = await NestFactory.create(/* 創建app的業務邏輯*/) app.use(cors({ origin:"http://localhost:8080", credentials:true })); await app.listen(3000) } bootstrap();
在上述的代碼中我們可以直接通過app.use來使用core這個express中的中間件。從而使得server端支持core跨域等。
初此之外,跟nestjs的中間件也完全保留了express中的中間件的特點:
在中間件中接受response和request作為參數,并且可以修改請求對象request和結果返回對象response
可以結束對于請求的處理,直接將請求的結果返回,也就是說可以在中間件中直接res.send等。
在該中間件處理完畢后,如果沒有將請求結果返回,那么可以通過next方法,將中間件傳遞給下一個中間件處理。
在nestjs中,中間件跟express中完全一樣,除了可以復用express中間件外,在nestjs中針對某一個特定的路由來使用中間件也十分的方便:
class ApplicationModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .forRoutes("cats"); } }
上面就是對于特定的路由url為/cats的時候,使用LoggerMiddleware中間件。
(2)、Exception filters異常過濾器??Exception filters異常過濾器可以捕獲在后端接受處理任何階段所跑出的異常,捕獲到異常后,然后返回處理過的異常結果給客戶端(比如返回錯誤碼,錯誤提示信息等等)。
??我們可以自定義一個異常過濾器,并且在這個異常過濾器中可以指定需要捕獲哪些異常,并且對于這些異常應該返回什么結果等,舉例一個自定義過濾器用于捕獲HttpException異常的例子。
@Catch(HttpException) export class HttpExceptionFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception.getStatus(); response .status(status) .json({ statusCode: status, timestamp: new Date().toISOString(), path: request.url, }); } }
??我們可以看到host是實現了ArgumentsHost接口的,在host中可以獲取運行環境中的信息,如果在http請求中那么可以獲取request和response,如果在socket中也可以獲取client和data信息。
??同樣的,對于異常過濾器,我們可以指定在某一個模塊中使用,或者指定其在全局使用等。
(3)Pipes管道??Pipes一般用戶驗證請求中參數是否符合要求,起到一個校驗參數的功能。
??比如我們對于一個請求中的某些參數,需要校驗或者轉化參數的類型:
@Injectable() export class ParseIntPipe implements PipeTransform{ transform(value: string, metadata: ArgumentMetadata): number { const val = parseInt(value, 10); if (isNaN(val)) { throw new BadRequestException("Validation failed"); } return val; } }
??上述的ParseIntPipe就可以把參數轉化成十進制的整型數字。我們可以這樣使用:
@Get(":id") async findOne(@Param("id", new ParseIntPipe()) id) { return await this.catsService.findOne(id); }
??對于get請求中的參數id,調用new ParseIntPipe方法來將id參數轉化成十進制的整數。
(4)Guards守衛??Guards守衛,其作用就是決定一個請求是否應該被處理函數接受并處理,當然我們也可以在middleware中間件中來做請求的接受與否的處理,與middleware相比,Guards可以獲得更加詳細的關于請求的執行上下文信息。
通常Guards守衛層,位于middleware之后,請求正式被處理函數處理之前。
下面是一個Guards的例子:
@Injectable() export class AuthGuard implements CanActivate { canActivate( context: ExecutionContext, ): boolean | Promise| Observable { const request = context.switchToHttp().getRequest(); return validateRequest(request); } }
這里的context實現了一個ExecutionContext接口,該接口中具有豐富的執行上下文信息。
export interface ArgumentsHost { getArgs= any[]>(): T; getArgByIndex (index: number): T; switchToRpc(): RpcArgumentsHost; switchToHttp(): HttpArgumentsHost; switchToWs(): WsArgumentsHost; } export interface ExecutionContext extends ArgumentsHost { getClass (): Type ; getHandler(): Function; }
除了ArgumentsHost中的信息外,ExecutionContext還包含了getClass用戶獲取對于某一個路由處理的,控制器。而getClass用于獲取返回對于指定路由后臺處理時的處理函數。
對于Guards處理函數,如果返回true,那么請求會被正常的處理,如果返回false那么請求會拋出異常。
(5)、interceptors攔截器?? 攔截器可以給每一個需要執行的函數綁定,攔截器將在該函數執行前或者執行后運行。可以轉換函數執行后返回的結果等。
概括來說:
interceptors攔截器在函數執行前或者執行后可以運行,如果在執行后運行,可以攔截函數執行的返回結果,修改參數等。
再來舉一個超時處理的例子:
@Injectable() export class TimeoutInterceptor implements NestInterceptor{ intercept( context:ExecutionContext, call$:Observable):Observable { return call$.pipe(timeout(5000)); } }
該攔截器可以定義在控制器上,可以處理超時請求。
四、nestjs的特點總結??最后總結一下nestjs的優缺。
nestjs的優點:
完美的支持typescript,因此可以使用日益繁榮的ts生態工具
兼容express中間件,因為express是最早出現的輕量級的node server端框架,nestjs能夠利用所有express的中間件,使其生態完善
層層處理,一定程度上可以約束代碼,比如何時使用中間件、何時需要使用guards守衛等。
依賴注入以及模塊化的思想,提供了完整的mvc的鏈路,使得代碼結構清晰,便于維護,這里的m是數據層可以通過modules的形式注入,比如通過typeorm的entity就可以在模塊中注入modules。
完美支持rxjs
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101663.html
摘要:三的洋蔥模型這里簡單講講在中是如何分層的,也就是說請求到達服務端后如何層層處理,直到響應請求并將結果返回客戶端。從而使得端支持跨域等。 ??最近已經使用過一段時間的nestjs,讓人寫著有一種java spring的感覺,nestjs可以使用express的所有中間件,此外完美的支持typescript,與數據庫關系映射typeorm配合使用可以快速的編寫一個接口網關。本文會介紹一下作...
摘要:在同等安全級別的情況下,發送文件的源作為引用地址,但是在降級的情況下不會發送。 什么是盜鏈 資源不在自己服務器上, 而通過技術手段, 把資源放置到自己的網站中, 通過這種方法盜取他人的資源. 什么是Referer Referer是http請求header的一部分, 當瀏覽器(或者模擬瀏覽器行為)向web服務器發送請求的時候,頭信息里有包含 Referer. 它表示當前接口的訪問來源...
摘要:本文從裝飾模式出發,聊聊中的裝飾器和注解。該函數的函數名。不提供元數據的支持。中的元數據操作可以通過包來實現對于元數據的操作。 ??隨著Typescript的普及,在KOA2和nestjs等nodejs框架中經常看到類似于java spring中注解的寫法。本文從裝飾模式出發,聊聊Typescipt中的裝飾器和注解。 什么是裝飾者模式 Typescript中的裝飾器 Typescr...
摘要:例子目錄結構如下代碼編寫工具采用目錄功能具體描述項目根目錄模塊安裝目錄。此例子對的版本和以上的版本也是有要求的,具體看官方文檔。有中文文檔的,但是那個網站有時候會訪問不了。不過在上有中文翻譯的托管。此例子完整代碼在上也可以查看。 Nest.js 入門小例子 前言:雖然使用官網的cli工具生成了一個基本的項目,但是由于正常開發中的項目的目錄結構往往需要自定義的,官方這個例子并不能滿足我們...
閱讀 1743·2021-09-22 15:25
閱讀 1307·2019-08-29 12:34
閱讀 1908·2019-08-26 13:57
閱讀 3188·2019-08-26 10:48
閱讀 1443·2019-08-26 10:45
閱讀 793·2019-08-23 18:23
閱讀 733·2019-08-23 18:01
閱讀 1945·2019-08-23 16:07