學(xué)習(xí)Nestjs 有一周的時(shí)間了,前幾天基本是對(duì)Typescipt 基本語法的一些學(xué)習(xí),再這里就不在講述了。當(dāng)然也對(duì)IOC這么偉大的編程思想有了初步的了解。今天在這里主要是想對(duì)Nestjs 的一周學(xué)習(xí)做一個(gè)總結(jié)和概括。

老外起名字 一般都有單詞本身的一些含義的。那我們就先來 講講什么是Nest。Nest 就是一組的完整的巢穴。這里基本兩層意思1. 單個(gè)nest 功能的完整性? 2. nest 和 nest 直接的功能的復(fù)制和嵌套性。我們要在閱讀全文的時(shí)候 要多從這兩個(gè)維度來思考整個(gè)架構(gòu)。有的時(shí)候我們就會(huì)豁然明朗。 那我們就切入正題吧。
Nest 是一個(gè)強(qiáng)悍的基于Node.js 的網(wǎng)站框架。它可以幫助我們不費(fèi)吹灰之力的去構(gòu)建高效和可控的應(yīng)用。在流行的 JavaScript基礎(chǔ),它是被構(gòu)建在Typescript語法 以及OOP 和 FP這些偉大的編程思想之上。由于它的底層用的是 Express 和 Socket.io ,有著強(qiáng)大的第三方插件。所以我們不需要擔(dān)心它的穩(wěn)健性和庫的不方便性。
核心思想:
Nest的核心思想就是提供了一個(gè)層與層直接的耦合度極小,抽象化極高的一個(gè)架構(gòu)體系。
配置程序:
Nest的功能使用了 ES6和ES7的語法(decorators, async / await)。我們可以方便的使用Babel 或 TypeScript。在這篇文章里面我講用 TypeScript。
?首先我們來配置一下 tsconfig.json?file:

讓我們從將最基本的開始。首先我們?nèi)?chuàng)建一個(gè)入口模塊 (app.module.ts):

在這個(gè)時(shí)候,模塊的元數(shù)據(jù)是空?({}), 因?yàn)槲覀冎皇菫榱讼劝殉绦蚺芷饋?。除了一個(gè)空模塊 我們一無所有 (例如?controlles or components 這些基本的模塊). 第二部 創(chuàng)建一個(gè)index.ts?(當(dāng)然 你可以隨意起名) ,然后使用? NestFactory 去創(chuàng)建一個(gè) Nest 實(shí)例程序 基于我們的類

添加一個(gè) Express 實(shí)例,如果我們想對(duì)express 實(shí)例有一個(gè)全周期的掌控的話,我們就必須要在創(chuàng)建對(duì)象的時(shí)候把 express 實(shí)例傳入到???NestFactory.create() 中,如下圖:

這就意味著,我們可以直接添加一些我在特有的配置 (比如添加一些像morgan?或?body-parser的插件).
首先我們要講的是 Controller
作為網(wǎng)站框架那一定要有收發(fā)包分發(fā)的功能模塊,在這個(gè)框架里Controller就是干這個(gè)的。
(官網(wǎng)定義:Controllers are responsible for handling incoming?requests?and returning?responses?to the client.)
所以說 Controllers 就是負(fù)責(zé)處理來的請(qǐng)求和回送的響應(yīng)到客戶端的一個(gè)功能模塊。
在Nest中,Controller 就是一個(gè)簡單的類,通常我們用?@Controller()?這個(gè)修飾符來操作

?好了,上面我們已經(jīng)設(shè)置好了一個(gè)程序的入口(3000端口)。那現(xiàn)在我們要繼續(xù)做進(jìn)一步的解析,為了分析出報(bào)文之間的不同 我們來搭建第一個(gè)服務(wù)入口 ( /users):

估計(jì)你已經(jīng)猜到了,我們的這個(gè)入口點(diǎn)油三種不同的形式

上面的寫法是不是有點(diǎn)太冗余不太友好。我們需要在不同的形式里面 重復(fù)這相同的故事。那有沒有一個(gè)簡潔的寫法呢? 哈哈 這種不費(fèi)吹灰之力的問題 對(duì)我們碼農(nóng)來說 答案肯定是 有的? Nest 允許我們直接傳入Metadata 到?@Controller()? 通過 修飾符? {path } 來作為相同路由的前綴如下:

在這里 我們要注意一下 Nest Controllers 里面的函數(shù)方法 他們是不是參數(shù),行為都一樣一樣滴?
(req,res,next)當(dāng)然 這些都是 Express 的一些知識(shí)。 我仔細(xì)看過 KOA2。 對(duì) Express 沒有仔細(xì)了解過 不過 看名字應(yīng)該不當(dāng)誤我們?nèi)?duì)它有個(gè)簡單的認(rèn)知。如果你想對(duì)他們有更多的學(xué)習(xí)請(qǐng)自行了解 或者等我研究完了 寫一篇 。 在這里 我們可以簡單的把 Next Controller 的方法看做是一種 Express 路由的封裝Users? Controller 已經(jīng)可是使用了,但是我們的模塊還不知道它的存在。讓我們創(chuàng)建一個(gè)?ApplicationModule,并且添加一些 Metadata(我就不再這里翻譯一些專業(yè)單詞 感覺真的不是一兩個(gè)單詞可以說明白的 不過我想我會(huì)單獨(dú)的寫一篇文章對(duì)專業(yè)詞匯的意思進(jìn)行解釋和說明一下。這也是我以前感覺要改進(jìn)的地方)

從上圖看來,我們只是把我們的Controller 插入到了一個(gè) Controllers的數(shù)組里面。 就這么簡單。從某種角度而言 這很像Serverless 的編程,我們不需要去配置什么服務(wù),只需要關(guān)注我們對(duì)事件消息的具體內(nèi)容的對(duì)碼,解析,和響應(yīng)。
Providers
在這里我們是基于最新的5 進(jìn)行解釋的。在4中 我們還稱之為component. 為什么從component改為Providers 呢。 我覺得?component 的相對(duì)關(guān)系比較強(qiáng)烈一點(diǎn) 組件是針對(duì)整體而言 是一層關(guān)系。 而Providers 就是角色闡明。只要我提供了某種功能,方法的給另外一個(gè)方的 都可以稱之為提供者。我覺得這個(gè)單詞能更好的表達(dá)出 IOC或者DI思想。(定義搬磚: almost everything may be considered as a provider – service, repository, factory, helper, and so on. All of them can?inject?dependencies through a constructor, meaning, they can create various relationships with each other. But in fact, a provider is nothing else than just a simple class annotated with an?@Injectable()?decorator.)這到底是個(gè)啥子意思啊。三句話簡明的告訴我們1.? 幾乎所有的東西都可以被視為 provider (還真是他媽的 世上萬物只要有價(jià)值就是被用的 哈哈 )
2. 在我們的Nes里它是怎么提供的 是通過constructor? 這個(gè)被服務(wù)的創(chuàng)造函數(shù)注入到被服務(wù)對(duì)象中的
3. 怎么成為Provider呢?? 來個(gè)制服吧,工牌??@Injectable()?

在上一章中 我們創(chuàng)造了一個(gè)簡單的 Controller (Userscontroller)。這個(gè)Controller可以訪問我們的數(shù)據(jù)(當(dāng)然這里我們還尚未接入任何數(shù)據(jù)庫 )。當(dāng)然這不是什么正途。在Nest 中要牢記一點(diǎn) 分層,盡量把功能單一的放在一層中,這也是AOP編程的一個(gè)重要思想。當(dāng)然在這里我們的Controllers 應(yīng)該只是處理消息的請(qǐng)求,然后下放更多復(fù)雜的任務(wù)到服務(wù)組件。那也是為什么我們要?jiǎng)?chuàng)建UsersService 這個(gè)組件。在實(shí)際中,UsersService 應(yīng)該合理的調(diào)用底層的方法(例如從UsersRepository 組件中調(diào)用 )。在這里我們沒有任何的數(shù)據(jù)庫。我們將使用fake data。

Nest? Provider 是一個(gè)很簡單的類。我們?nèi)绻胱屢粋€(gè)類變成Provider? 只需要 給一個(gè)工號(hào)標(biāo)簽? @Injectable() 。寫到這里有沒有發(fā)現(xiàn)什么問題嗎?? 上圖的工號(hào)標(biāo)簽 我是不是寫錯(cuò)了? 那是4中的寫法。在5中 換成?@Injectable()? 就好了。還有上圖當(dāng)中 我們是不是在?getUser() 這個(gè)方法當(dāng)中 用到了?HttpException 這個(gè)異常類。它是 Nest 內(nèi)置的異常,傳入兩個(gè)參數(shù)就好了?error message?and?status code
好了,我們的service 已經(jīng)準(zhǔn)備就緒。讓我們?cè)赨sersController中調(diào)用它

Notice: 我刪除了next 參數(shù)在我們的方法中。因?yàn)樵谶@里我們不會(huì)用到它?
如上圖所示,?UsersService? 被注入到了?constructor. 當(dāng)然在最新的版本中 我們還可以 用TS的語法 給一個(gè) readonly 的讀寫權(quán)限的檢驗(yàn)
用聲明的TS語法就是好? 簡單一行 就實(shí)現(xiàn)了 DI 的注入方式

簡單的興許之余,千萬別忘了在?tsconfig.json中把 emitDecoratorMetadata 這個(gè)屬性設(shè)置為true。否則上面的寫法不會(huì)生效的。
到目前為止,我們的程序還不能開始工作。 為什么呢? 因?yàn)镹est 不知道任何 UserService 的信息。這個(gè)Provider(Service) 還不是ApplicationModule的一部分。那我們就應(yīng)該配置中添加
同理這里的寫法是4的? 5中 需要 替換成?providers

好了 讓我們跑一下我們的代碼。不過你會(huì)發(fā) 還是不能很好的跑起來。當(dāng)adduser 的時(shí)候。
為什么呢?因?yàn)楫?dāng)我們嘗試去解析 請(qǐng)求報(bào)文的時(shí)候(req.body.user)并沒有相對(duì)于的中間件對(duì)報(bào)文提前解析。 來 我們按照一下插件

然后設(shè)置一下 Express 的配置

Async / await
Nest 是支持? ES7中的async / await 的功能的。所以我們改一下書寫風(fēng)格

是不是看著舒服多了?
Modules
搬磚定義:(A module is a class annotated with a?@Module()?decorator. The?@Module()?decorator provides metadata that?Nest?makes use of to organize the application structure.)?一句話 這個(gè)修飾器 就是用來組織程序架構(gòu)的

目前 我們的程序 ApplicationModule(碰到compents 就自行更改成Providers 吧 不啰嗦了):

模塊封裝了每一個(gè)依賴。這就意味著我們不可以使用它的Provider 和 controller 在它之外。想交流必須通過 他們的module 這個(gè)模塊。 這種方法也讓Nest 在微服務(wù)的場(chǎng)景中游刃有余,清晰明了。每一個(gè)模塊可以導(dǎo)入別的模塊。從某種意義上 我們可以理解為Nest Modules 是一個(gè)樹形 modules
那我們把我們以前寫的UsersController?和 UsersService 挪到?UsersModule. 只需要簡單的創(chuàng)建一個(gè)文件 例如(.?users.module.ts) ,內(nèi)容如下:

然后我們把UsersModule?導(dǎo)入到 ApplicationModule?(我們的主程序模塊):

一切搞定! 是不是很明晰。
通過上面的手法 我們可以非常簡潔自然的把我們的代碼劃分成 獨(dú)立 可重用的 模塊
Middlewares
搬磚定義:( The middleware is a function which is called?before?the route handler. Middleware functions have access to the?request?and?response?objects, and the?next?middleware function in the application’s request-response cycle. The?next?middleware function is commonly denoted by a variable named?next.)
1.? ?它不是類 是函數(shù)??
2.? 它在路由處理之前被調(diào)用
3.? 它可以訪問和更改請(qǐng)求和回復(fù)的對(duì)象
4.? 如果你不調(diào)用 next()的話,請(qǐng)求報(bào)文將不會(huì)被 路由處理

讓我們來搭建一個(gè)假的認(rèn)證中間件。我們將用?X-Access-Token?HTTP header 來提供名字 (只是舉例現(xiàn)實(shí)中別這么用 ).

記住以下幾點(diǎn):
1.? ?@Injectable() 是來告訴 Nest? 這是一個(gè) middleware
2.? 使用?NestMiddleware 這個(gè)接口,可以讓我們?nèi)?shí)現(xiàn)?resolve()? 這個(gè)方法
middlewares? 可以像 Provider 一樣 通過創(chuàng)造函數(shù)來注入依賴對(duì)體中。middlewares必須包含?resolve() 這個(gè)方法,它是用來返回給另外一個(gè)高階函數(shù)的 ,這也就意味著 中間件有可能不只一個(gè) 。當(dāng)然我們還可以引入 中間件函數(shù)類型 對(duì) 我們寫的resolve函數(shù)的返回做驗(yàn)證
友情提示:? 我們添加getUsers?方法 到? UsersService, 用來返回一個(gè)users 的數(shù)組。
我們已經(jīng)準(zhǔn)備好了 一個(gè)中間件了,那我們下一步就是要把它注入到調(diào)用的地方去?
如下:

從上圖得知,Modules 有額外的方法 configure().? 這個(gè)方法接收 MiddlewareBuilder 類型的對(duì)象作為參數(shù), 它可以有效的幫助我們?nèi)ヅ渲?middlewares. 這個(gè)對(duì)象的apply()? 方法將可以收到無限數(shù)的中間件(它使用了 Spread 操作符,所以傳參的時(shí)候用逗號(hào)分隔就好了).?apply() 這個(gè)函數(shù)返回的是一個(gè)對(duì)象,這個(gè)對(duì)象包含了兩個(gè)方法:
1. forRoutes()?–我們使用這個(gè)方法傳入無限制的Controllers 或者是其他的對(duì)象,用逗號(hào)做分隔就好了??
2.? with?–? 我們可以用這個(gè)方法傳入客戶參數(shù)給 resolve()這個(gè)方法?
更新一下:5的代碼里面 好像改成了Middlewarecustomer,看了nest 代碼 interface 下也沒有builder 那個(gè)東西了。
When you pass?UsersController?in?forRoutes?method, Nest will setup middleware for each route in controller:
當(dāng)我們把?UsersController 傳入?forRoutes?這個(gè)方法的時(shí)候, Nest 將會(huì)為controller 中的每一個(gè)路由設(shè)置中間件

但是我們也可以直接定義帶有路徑的中間件,如下:

Chaining
在這里 apply 和 forRoutes 的組合 可以做無限制的疊加。

Ordering
中間件的調(diào)用順序和他們的在數(shù)組中的順序相同。
微服務(wù):?
微服務(wù)是通過消息來傳遞的。一般我們用TCP來傳遞消息。也可以自我配置 用Redis 也可以

正是由于Nest??microservice? 是通過TCP等協(xié)議來交互的。那就意味著 @RequestMapping()這一套Post ,Get 的解析機(jī)制就不可以再使用了。因?yàn)樗麄兪腔贖TTP的。那我們應(yīng)該怎么識(shí)別消息內(nèi)容呢?? Pattern 樣式 是我們唯一的選擇。什么是Pattern呢? 它是一個(gè)對(duì)象,字符串,數(shù)字 只要定義好格式就好
創(chuàng)建 MathController:

好了今天就寫到這里了。如果有拼寫錯(cuò)誤 或者寫的不對(duì)的地方 請(qǐng)大家多多留言給我。
隨著繼續(xù)的學(xué)習(xí) 我們把剩下的一下東西 一點(diǎn)點(diǎn)的寫出來。
晚安!