
如何快速入門VUE3.0:進入學習
Nest.js 是一個Nodejs 的後端框架,它對express 等http 平台做了一層封裝,解決了架構問題。它提供了express 沒有的MVC、IOC、AOP 等架構特性,讓程式碼更容易維護、擴充。
這裡的MVC、IOC、AOP 都是啥意思呢?讓我們分別來看看:
MVC 是Model View Controller 的簡寫。 MVC 架構下,請求會先傳送給Controller,由它調度Model 層的Service 來完成業務邏輯,然後回傳對應的View。

Nest.js 提供了@Controller 裝飾器用來宣告Controller:

而Service 會用@Injectable 裝飾器來聲明:

透過@Controller、@Injectable 裝飾器宣告的class 會被Nest.js 掃描,建立對應的物件並加到一個容器裡,這些所有的物件會根據建構器裡宣告的依賴自動注入,也就是DI(dependency inject ),這種想法叫做IOC(Inverse Of Control)。
IOC 架構的好處是不需要手動建立物件和根據依賴關係傳入不同物件的建構器中,一切都是自動掃描並建立、注入的。
此外,Nest.js 也提供了AOP (Aspect Oriented Programming)的能力,也就是面向切面程式設計的能力:
AOP 是什麼意思呢?什麼是面向切面程式設計呢?
一個請求過來,可能會經過Controller(控制器)、Service(服務)、Repository(資料庫存取) 的邏輯:

如果想在這個呼叫連結裡加入一些通用邏輯該怎麼加呢?例如日誌記錄、權限控制、異常處理等。
容易想到的是直接改造Controller 層程式碼,加入這段邏輯。這樣可以,但是不優雅,因為這些通用的邏輯侵入了業務邏輯裡面。能不能透明的給這些業務邏輯加上日誌、權限等處理呢?
那是不是可以在呼叫Controller 之前和之後加入一個執行通用邏輯的階段呢?
比如這樣:

這樣的橫向擴展點就叫做切面,這種透明的加入一些切面邏輯的程式設計方式就叫做AOP (面向切面程式)。
AOP 的好處是可以把一些通用邏輯分離到切面中,保持業務邏輯的存粹性,這樣切面邏輯可以復用,還可以動態的增刪
其實Express 的中間件的洋蔥模型也是一種AOP 的實現,因為你可以透明的在外麵包一層,加入一些邏輯,內層感知不到。
而Nest.js 實現AOP 的方式更多,一共有五種,包括Middleware、Guard、Pipe、Inteceptor、ExceptionFilter:、
Nest.js 基於Express 自然也可以使用中間件,但是做了進一步的細分,分為了全域中間件和路由中間件:
全域中間件就是Express 的那種中間件,在請求之前和之後加入一些處理邏輯,每個請求都會走到這裡:

路由中間件則是針對某個路由來說的,範圍更小一些:

這個是直接繼承了Express 的概念,比較容易理解。
再來看一些Nest.js 擴充的概念,像是Guard:
Guard 是路由守衛的意思,可以用來在呼叫某個Controller 之前判斷權限,回傳true 或flase 來決定是否放行:

創造Guard 的方式是這樣的:

Guard 要實作CanActivate 接口,實作canActive 方法,可以從context 拿到請求的信息,然後做一些權限驗證等處理之後返回true 或false。
透過@Injectable 裝飾器加到IOC 容器中,然後就可以在某個Controller 啟用了:

Controller 本身不需要做啥修改,卻透明的加上了權限判斷的邏輯,這就是AOP 架構的好處。
而且,就像Middleware 支援全域等級和路由等級一樣,Guard 也可以全域啟用:

Guard 可以抽離路由的存取控制邏輯,但不能對請求、回應做修改,這種邏輯可以使用Interceptor:
Interceptor 是攔截器的意思,可以在目標Controller 方法前後加入一些邏輯:

創建Inteceptor 的方式是這樣的:

Interceptor 要實作NestInterceptor 接口,實作intercept 方法,呼叫next.handle() 就會呼叫目標Controller,可以在之前和之後加入一些處理邏輯。
Controller 之前之後的處理邏輯可能是異步的。 Nest.js 裡透過rxjs 來組織它們,所以可以使用rxjs 的各種operator。
Interceptor 支援每個路由單獨啟用,只作用於某controller,也同樣支援全域啟用,作用於全部controller:


除了路由的權限控制、目標Controller 之前之後的處理這些都是通用邏輯外,對參數的處理也是一個通用的邏輯,所以Nest.js 也抽出了對應的切面,也就是Pipe: Pipe
是管道的意思,用來對參數做一些驗證和轉換:

創建Pipe 的方式是這樣的:

Pipe 要實作PipeTransform 接口,實作transform 方法,裡面可以對傳入的參數值value 做參數驗證,例如格式、類型是否正確,不正確就拋出例外。也可以做轉換,回傳轉換後的值。
內建的有8 個Pipe,從名字就能看出它們的意思:
ValidationPipeParseIntPipeParseBoolPipeParseArrayPipeParseUUIDPipeDefaultValuePipeParseEnumPipeParseFloatPipe同樣,Pipe 可以只對某個路由生效,也可以對每個路由都生效:


不管是Pipe、Guard、Interceptor 還是最終呼叫的Controller,過程中都可以拋出一些異常,如何對某種異常做出某種回應?
這種異常到回應的映射也是一種通用邏輯,Nest.js 提供了ExceptionFilter 來支援:
ExceptionFilter 可以對拋出的例外做處理,傳回對應的回應:

建立ExceptionFilter的形式是這樣的:

首先要實現ExceptionFilter 接口,實現catch 方法,就可以攔截異常了,但是要攔截什麼異常還需要用@Catch 裝飾器來聲明,攔截了異常之後,可以異常對應的響應,給用戶更友好的提示。
當然,也不是所有的例外都會處理,只有繼承HttpException 的例外才會被ExceptionFilter 處理,Nest.js 內建了許多HttpException 的子類別:
UnprocessableExceptionPayloadTooLargeExceptionRequestTimeoutExceptionConflictExceptionGoneExceptionBadRequestExceptionUnauthorizedExceptionNotFoundExceptionForbiddenExceptionNotAcceptableExceptionUnsupportedMediaTypeExceptionInternalServerErrorExceptionNotImplementedExceptionBadGatewayExceptionServiceUnavailableExceptionGatewayTimeoutException當然,也可以自己擴充:

Nest.js 透過這樣的方式實現了異常到回應的對應關係,程式碼裡只要拋出不同的HttpException,就會回傳對應的回應,很方便。
同樣,ExceptionFilter 也可以選擇全域生效或某個路由生效:
某個路由:

全局:

我們了解了Nest.js 提供的AOP 的機制,但它們的順序關係是怎麼樣的呢?
Middleware、Guard、Pipe、Interceptor、ExceptionFilter 都可以透明的添加某種處理邏輯到某個路由或全部路由,這就是AOP 的好處。
但是它們之間的順序關係是什麼呢?
呼叫關係這個得看源碼了。
對應的源碼是這樣的:

很明顯,進入這個路由的時候,會先呼叫Guard,判斷是否有權限等,如果沒有權限,這裡就拋異常了:

拋出的HttpException 會被ExceptionFilter 處理。
如果有權限,就會調用到攔截器,攔截器組織了一個鏈條,一個個的調用,最後會調用的controller 的方法:

在呼叫controller 方法之前,會使用pipe 對參數做處理:

會對每個參數做轉換:

ExceptionFilter 的呼叫時機很容易想到,就是在回應之前先對異常做一次處理。
而Middleware 是express 中的概念,Nest.js 只是繼承了下,那個是在最外層被呼叫。
這就是這幾種AOP 機制的呼叫順序。把這些理清楚,就算是對Nest.js 有很好的掌握了。
Nest.js 是基於express 這種http 平台做了一層封裝,應用了MVC、IOC、AOP 等架構思想。
MVC 是Model、View Controller 的劃分,要求先經過Controller,然後呼叫Model 層的Service、Repository 完成業務邏輯,最後再回傳對應的View。
IOC 是指Nest.js 會自動掃描帶有@Controller、@Injectable 裝飾器的類,創建它們的對象,並根據依賴關係自動注入它依賴的對象,免去了手動創建和組裝對象的麻煩。
AOP 則是把通用邏輯抽離出來,透過切面的方式加入某個地方,可以重複使用和動態增刪切面邏輯。
Nest.js 的Middleware、Guard、Interceptor、Pipe、ExceptionFileter 都是AOP 思想的實現,只不過是不同位置的切面,它們都可以靈活的作用在某個路由或全部路由,這就是AOP 的優勢。
我們透過原始碼來看了它們的呼叫順序,Middleware 是Express 的概念,在最外層,到了某個路由之後,會先呼叫Guard,Guard 用於判斷路由有沒有權限訪問,然後會呼叫Interceptor,對Contoller前後擴充一些邏輯,在到達目標Controller 之前,也會呼叫Pipe 來對參數做驗證和轉換。所有的HttpException 的例外都會被ExceptionFilter 處理,回傳不同的回應。
Nest.js 就是透過這種AOP 的架構方式,實現了鬆散耦合、易於維護和擴展的架構。
AOP 架構的好處,你感受到了麼?