學(xué)習(xí)Nestjs 有一周的時間了,前幾天基本是對Typescipt 基本語法的一些學(xué)習(xí)撵割,再這里就不在講述了弄诲。當(dāng)然也對IOC這么偉大的編程思想有了初步的了解菱鸥。今天在這里主要是想對Nestjs 的一周學(xué)習(xí)做一個總結(jié)和概括澎蛛。
老外起名字 一般都有單詞本身的一些含義的抚垄。那我們就先來 講講什么是Nest。Nest 就是一組的完整的巢穴谋逻。這里基本兩層意思1. 單個nest 功能的完整性? 2. nest 和 nest 直接的功能的復(fù)制和嵌套性呆馁。我們要在閱讀全文的時候 要多從這兩個維度來思考整個架構(gòu)。有的時候我們就會豁然明朗毁兆。 那我們就切入正題吧浙滤。
Nest 是一個強悍的基于Node.js 的網(wǎng)站框架。它可以幫助我們不費吹灰之力的去構(gòu)建高效和可控的應(yīng)用气堕。在流行的 JavaScript基礎(chǔ)纺腊,它是被構(gòu)建在Typescript語法 以及OOP 和 FP這些偉大的編程思想之上。由于它的底層用的是 Express 和 Socket.io 茎芭,有著強大的第三方插件摹菠。所以我們不需要擔(dān)心它的穩(wěn)健性和庫的不方便性。
核心思想:
Nest的核心思想就是提供了一個層與層直接的耦合度極小骗爆,抽象化極高的一個架構(gòu)體系。
配置程序:
Nest的功能使用了 ES6和ES7的語法(decorators, async / await)蔽介。我們可以方便的使用Babel 或 TypeScript摘投。在這篇文章里面我講用 TypeScript。
?首先我們來配置一下 tsconfig.json?file:
讓我們從將最基本的開始虹蓄。首先我們?nèi)?chuàng)建一個入口模塊 (app.module.ts):
在這個時候犀呼,模塊的元數(shù)據(jù)是空?({}), 因為我們只是為了先把程序跑起來 。除了一個空模塊 我們一無所有 (例如?controlles or components 這些基本的模塊). 第二部 創(chuàng)建一個index.ts?(當(dāng)然 你可以隨意起名) 薇组,然后使用? NestFactory 去創(chuàng)建一個 Nest 實例程序 基于我們的類
添加一個 Express 實例外臂,如果我們想對express 實例有一個全周期的掌控的話,我們就必須要在創(chuàng)建對象的時候把 express 實例傳入到???NestFactory.create() 中律胀,如下圖:
這就意味著宋光,我們可以直接添加一些我在特有的配置 (比如添加一些像morgan?或?body-parser的插件).
首先我們要講的是 Controller
作為網(wǎng)站框架那一定要有收發(fā)包分發(fā)的功能模塊,在這個框架里Controller就是干這個的炭菌。
(官網(wǎng)定義:Controllers are responsible for handling incoming?requests?and returning?responses?to the client.)
所以說 Controllers 就是負(fù)責(zé)處理來的請求和回送的響應(yīng)到客戶端的一個功能模塊罪佳。
在Nest中,Controller 就是一個簡單的類黑低,通常我們用?@Controller()?這個修飾符來操作
?好了赘艳,上面我們已經(jīng)設(shè)置好了一個程序的入口(3000端口)。那現(xiàn)在我們要繼續(xù)做進(jìn)一步的解析,為了分析出報文之間的不同 我們來搭建第一個服務(wù)入口 ( /users):
估計你已經(jīng)猜到了蕾管,我們的這個入口點油三種不同的形式
上面的寫法是不是有點太冗余不太友好枷踏。我們需要在不同的形式里面 重復(fù)這相同的故事。那有沒有一個簡潔的寫法呢掰曾? 哈哈 這種不費吹灰之力的問題 對我們碼農(nóng)來說 答案肯定是 有的? Nest 允許我們直接傳入Metadata 到?@Controller()? 通過 修飾符? {path } 來作為相同路由的前綴如下:
在這里 我們要注意一下 Nest Controllers 里面的函數(shù)方法 他們是不是參數(shù)旭蠕,行為都一樣一樣滴?
(req,res,next)當(dāng)然 這些都是 Express 的一些知識婴梧。 我仔細(xì)看過 KOA2下梢。 對 Express 沒有仔細(xì)了解過 不過 看名字應(yīng)該不當(dāng)誤我們?nèi)λ袀€簡單的認(rèn)知。如果你想對他們有更多的學(xué)習(xí)請自行了解 或者等我研究完了 寫一篇 塞蹭。 在這里 我們可以簡單的把 Next Controller 的方法看做是一種 Express 路由的封裝Users? Controller 已經(jīng)可是使用了孽江,但是我們的模塊還不知道它的存在。讓我們創(chuàng)建一個?ApplicationModule番电,并且添加一些 Metadata(我就不再這里翻譯一些專業(yè)單詞 感覺真的不是一兩個單詞可以說明白的 不過我想我會單獨的寫一篇文章對專業(yè)詞匯的意思進(jìn)行解釋和說明一下岗屏。這也是我以前感覺要改進(jìn)的地方)
從上圖看來,我們只是把我們的Controller 插入到了一個 Controllers的數(shù)組里面漱办。 就這么簡單这刷。從某種角度而言 這很像Serverless 的編程,我們不需要去配置什么服務(wù)娩井,只需要關(guān)注我們對事件消息的具體內(nèi)容的對碼暇屋,解析,和響應(yīng)洞辣。
Providers
在這里我們是基于最新的5 進(jìn)行解釋的咐刨。在4中 我們還稱之為component. 為什么從component改為Providers 呢。 我覺得?component 的相對關(guān)系比較強烈一點 組件是針對整體而言 是一層關(guān)系扬霜。 而Providers 就是角色闡明定鸟。只要我提供了某種功能,方法的給另外一個方的 都可以稱之為提供者著瓶。我覺得這個單詞能更好的表達(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.)這到底是個啥子意思啊。三句話簡明的告訴我們1.? 幾乎所有的東西都可以被視為 provider (還真是他媽的 世上萬物只要有價值就是被用的 哈哈 )
2. 在我們的Nes里它是怎么提供的 是通過constructor? 這個被服務(wù)的創(chuàng)造函數(shù)注入到被服務(wù)對象中的
3. 怎么成為Provider呢材原?? 來個制服吧沸久,工牌??@Injectable()?
在上一章中 我們創(chuàng)造了一個簡單的 Controller (Userscontroller)。這個Controller可以訪問我們的數(shù)據(jù)(當(dāng)然這里我們還尚未接入任何數(shù)據(jù)庫 )余蟹。當(dāng)然這不是什么正途麦向。在Nest 中要牢記一點 分層,盡量把功能單一的放在一層中客叉,這也是AOP編程的一個重要思想诵竭。當(dāng)然在這里我們的Controllers 應(yīng)該只是處理消息的請求话告,然后下放更多復(fù)雜的任務(wù)到服務(wù)組件。那也是為什么我們要創(chuàng)建UsersService 這個組件卵慰。在實際中沙郭,UsersService 應(yīng)該合理的調(diào)用底層的方法(例如從UsersRepository 組件中調(diào)用 )。在這里我們沒有任何的數(shù)據(jù)庫裳朋。我們將使用fake data病线。
Nest? Provider 是一個很簡單的類。我們?nèi)绻胱屢粋€類變成Provider? 只需要 給一個工號標(biāo)簽? @Injectable() 鲤嫡。寫到這里有沒有發(fā)現(xiàn)什么問題嗎送挑?? 上圖的工號標(biāo)簽 我是不是寫錯了? 那是4中的寫法暖眼。在5中 換成?@Injectable()? 就好了惕耕。還有上圖當(dāng)中 我們是不是在?getUser() 這個方法當(dāng)中 用到了?HttpException 這個異常類。它是 Nest 內(nèi)置的異常诫肠,傳入兩個參數(shù)就好了?error message?and?status code
好了司澎,我們的service 已經(jīng)準(zhǔn)備就緒。讓我們在UsersController中調(diào)用它
Notice: 我刪除了next 參數(shù)在我們的方法中栋豫。因為在這里我們不會用到它?
如上圖所示,?UsersService? 被注入到了?constructor. 當(dāng)然在最新的版本中 我們還可以 用TS的語法 給一個 readonly 的讀寫權(quán)限的檢驗
用聲明的TS語法就是好? 簡單一行 就實現(xiàn)了 DI 的注入方式
簡單的興許之余挤安,千萬別忘了在?tsconfig.json中把 emitDecoratorMetadata 這個屬性設(shè)置為true。否則上面的寫法不會生效的丧鸯。
到目前為止蛤铜,我們的程序還不能開始工作。 為什么呢丛肢? 因為Nest 不知道任何 UserService 的信息昂羡。這個Provider(Service) 還不是ApplicationModule的一部分。那我們就應(yīng)該配置中添加
同理這里的寫法是4的? 5中 需要 替換成?providers
好了 讓我們跑一下我們的代碼摔踱。不過你會發(fā) 還是不能很好的跑起來。當(dāng)adduser 的時候怨愤。
為什么呢派敷?因為當(dāng)我們嘗試去解析 請求報文的時候(req.body.user)并沒有相對于的中間件對報文提前解析。 來 我們按照一下插件
然后設(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òu)的
目前 我們的程序 ApplicationModule(碰到compents 就自行更改成Providers 吧 不啰嗦了):
模塊封裝了每一個依賴。這就意味著我們不可以使用它的Provider 和 controller 在它之外差导。想交流必須通過 他們的module 這個模塊试躏。 這種方法也讓Nest 在微服務(wù)的場景中游刃有余,清晰明了设褐。每一個模塊可以導(dǎo)入別的模塊颠蕴。從某種意義上 我們可以理解為Nest Modules 是一個樹形 modules
那我們把我們以前寫的UsersController?和 UsersService 挪到?UsersModule. 只需要簡單的創(chuàng)建一個文件 例如(.?users.module.ts) 泣刹,內(nèi)容如下:
然后我們把UsersModule?導(dǎo)入到 ApplicationModule?(我們的主程序模塊):
一切搞定! 是不是很明晰犀被。
通過上面的手法 我們可以非常簡潔自然的把我們的代碼劃分成 獨立 可重用的 模塊
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.? 它可以訪問和更改請求和回復(fù)的對象
4.? 如果你不調(diào)用 next()的話椅您,請求報文將不會被 路由處理
讓我們來搭建一個假的認(rèn)證中間件。我們將用?X-Access-Token?HTTP header 來提供名字 (只是舉例現(xiàn)實中別這么用 ).
記住以下幾點:
1.? ?@Injectable() 是來告訴 Nest? 這是一個 middleware
2.? 使用?NestMiddleware 這個接口寡键,可以讓我們?nèi)崿F(xiàn)?resolve()? 這個方法
middlewares? 可以像 Provider 一樣 通過創(chuàng)造函數(shù)來注入依賴對體中掀泳。middlewares必須包含?resolve() 這個方法,它是用來返回給另外一個高階函數(shù)的 西轩,這也就意味著 中間件有可能不只一個 员舵。當(dāng)然我們還可以引入 中間件函數(shù)類型 對 我們寫的resolve函數(shù)的返回做驗證
友情提示:? 我們添加getUsers?方法 到? UsersService, 用來返回一個users 的數(shù)組。
我們已經(jīng)準(zhǔn)備好了 一個中間件了藕畔,那我們下一步就是要把它注入到調(diào)用的地方去?
如下:
從上圖得知马僻,Modules 有額外的方法 configure().? 這個方法接收 MiddlewareBuilder 類型的對象作為參數(shù), 它可以有效的幫助我們?nèi)ヅ渲?middlewares. 這個對象的apply()? 方法將可以收到無限數(shù)的中間件(它使用了 Spread 操作符劫流,所以傳參的時候用逗號分隔就好了).?apply() 這個函數(shù)返回的是一個對象巫玻,這個對象包含了兩個方法:
1. forRoutes()?–我們使用這個方法傳入無限制的Controllers 或者是其他的對象,用逗號做分隔就好了??
2.? with?–? 我們可以用這個方法傳入客戶參數(shù)給 resolve()這個方法?
更新一下:5的代碼里面 好像改成了Middlewarecustomer祠汇,看了nest 代碼 interface 下也沒有builder 那個東西了仍秤。
When you pass?UsersController?in?forRoutes?method, Nest will setup middleware for each route in controller:
當(dāng)我們把?UsersController 傳入?forRoutes?這個方法的時候, Nest 將會為controller 中的每一個路由設(shè)置中間件
但是我們也可以直接定義帶有路徑的中間件可很,如下:
Chaining
在這里 apply 和 forRoutes 的組合 可以做無限制的疊加诗力。
Ordering
中間件的調(diào)用順序和他們的在數(shù)組中的順序相同。
微服務(wù):?
微服務(wù)是通過消息來傳遞的我抠。一般我們用TCP來傳遞消息苇本。也可以自我配置 用Redis 也可以
正是由于Nest??microservice? 是通過TCP等協(xié)議來交互的。那就意味著 @RequestMapping()這一套Post 菜拓,Get 的解析機制就不可以再使用了瓣窄。因為他們是基于HTTP的。那我們應(yīng)該怎么識別消息內(nèi)容呢纳鼎?? Pattern 樣式 是我們唯一的選擇俺夕。什么是Pattern呢? 它是一個對象贱鄙,字符串劝贸,數(shù)字 只要定義好格式就好
創(chuàng)建 MathController:
好了今天就寫到這里了。如果有拼寫錯誤 或者寫的不對的地方 請大家多多留言給我逗宁。
隨著繼續(xù)的學(xué)習(xí) 我們把剩下的一下東西 一點點的寫出來映九。
晚安!