4.2 服務(wù)器的相關(guān)知識(shí)
因?yàn)檎嬲暮笈_(tái)應(yīng)用是需要在服務(wù)器上面運(yùn)行的,因此有必要在這里補(bǔ)充一下關(guān)于服務(wù)器的知識(shí)。
我們用到的大部分網(wǎng)絡(luò)應(yīng)用,如APP、Web網(wǎng)站、PC客戶(hù)端甚至銀行取款機(jī)的背后都提供了一個(gè)強(qiáng)大的后臺(tái)服務(wù),無(wú)論是電商、銀行抑或游戲服務(wù),對(duì)于一個(gè)用戶(hù)來(lái)講,我們感受到的僅僅是數(shù)據(jù)的傳遞和渲染,但是這些數(shù)據(jù)是怎么被匯總、計(jì)算或者存儲(chǔ)的,大部分人無(wú)從得知,甚至感覺(jué)這一切是非常自然的事情。然則,事實(shí)并非如此。
目前,大部分網(wǎng)絡(luò)應(yīng)用都是搭建在分布式集群網(wǎng)絡(luò)服務(wù)器上的,成千上萬(wàn)的服務(wù)器相互之間協(xié)調(diào),聯(lián)系,共享數(shù)據(jù),構(gòu)成了一個(gè)覆蓋每一個(gè)用戶(hù)的服務(wù)網(wǎng)絡(luò)。
4.2.1 服務(wù)器概念
服務(wù)器就是一臺(tái)高性能的計(jì)算機(jī),它的高性能主要體現(xiàn)在高速度的運(yùn)算能力、長(zhǎng)時(shí)間的可靠運(yùn)行、強(qiáng)大的外部數(shù)據(jù)吞吐能力等方面。服務(wù)器的構(gòu)成與家用電腦基本相似,包括處理器、硬盤(pán)、內(nèi)存、系統(tǒng)總線等,但它們是針對(duì)具體的網(wǎng)絡(luò)應(yīng)用特別制定的,因而服務(wù)器與微機(jī)在處理能力、穩(wěn)定性、可靠性、安全性、可擴(kuò)展性、可管理性等方面差異很大。
我們搭建的后臺(tái)應(yīng)用在生產(chǎn)環(huán)境下都是搭建在服務(wù)器上面的,因?yàn)榉?wù)器可以保證24小時(shí)不間斷工作,而且能夠保證很好地網(wǎng)絡(luò)吞吐能力。而對(duì)于每一個(gè)用戶(hù)來(lái)講,在資源有限的情況的下,一個(gè)服務(wù)器的資源是被多個(gè)用戶(hù)共享的,因此在硬件限制的情況下,就需要考慮軟件的編寫(xiě)來(lái)優(yōu)化用戶(hù)的請(qǐng)求和處理,保證每個(gè)用戶(hù)能夠公平流暢的享受服務(wù)。
服務(wù)器長(zhǎng)這個(gè)樣子,里面安裝的是Linux或WindowsServer系統(tǒng)哦
4.2.2 TCP/IP和HTTP協(xié)議
一個(gè)沒(méi)有聯(lián)網(wǎng)的單個(gè)服務(wù)器,除了能夠當(dāng)做一個(gè)大型的計(jì)算機(jī)之外,沒(méi)有任何用途。只有把服務(wù)器之間聯(lián)網(wǎng),服務(wù)器和用戶(hù)的電腦之間聯(lián)網(wǎng)才能讓用戶(hù)和網(wǎng)絡(luò)服務(wù)器提供商真正的建立聯(lián)系。那么用戶(hù)的瀏覽器和提供商的服務(wù)器的Web應(yīng)用是怎么通過(guò)網(wǎng)絡(luò)取得聯(lián)系的呢?
為了解決這個(gè)問(wèn)題,計(jì)算機(jī)網(wǎng)絡(luò)分層體系和網(wǎng)絡(luò)協(xié)議應(yīng)運(yùn)而生:
互聯(lián)網(wǎng)協(xié)議按照功能不同分為OSI七層或TCP/IP五層或TCP/IP四層,搞清楚了每層的主要協(xié)議
就理解了整個(gè)互聯(lián)網(wǎng)通信的原理。每層都運(yùn)行特定的協(xié)議,越往上越靠近用戶(hù),越往下越靠近硬件。
什么是協(xié)議?
所謂協(xié)議就是互聯(lián)網(wǎng)設(shè)備之間通信需要遵循的一套規(guī)則或者標(biāo)準(zhǔn),消息的發(fā)送者和接收者都需要遵循這個(gè)標(biāo)準(zhǔn)才能理解對(duì)方表達(dá)的意思。

數(shù)據(jù)鏈路層:以太網(wǎng)協(xié)議屬于數(shù)據(jù)鏈路層協(xié)議。
?以太網(wǎng)協(xié)議規(guī)定:
一組信號(hào)構(gòu)成一個(gè)數(shù)據(jù)包,叫做‘幀’
每一數(shù)據(jù)幀分成:報(bào)頭head和數(shù)據(jù)data兩部分
?
headdata
?
head包含:(固定18個(gè)字節(jié))
發(fā)送者/源地址,6個(gè)字節(jié)
接收者/目標(biāo)地址,6個(gè)字節(jié)
數(shù)據(jù)類(lèi)型,6個(gè)字節(jié)
data包含:(最短46字節(jié),最長(zhǎng)1500字節(jié))
數(shù)據(jù)包的具體內(nèi)容
目標(biāo)地址/源地址指代的就是mac地址,也就是我們電腦或通信設(shè)備的網(wǎng)卡地址,mac地址在全世界都是唯一的。
有了mac地址,同一網(wǎng)絡(luò)內(nèi)的兩臺(tái)主機(jī)就可以通信了,但以太網(wǎng)協(xié)議采用最原始的方式——廣播的方式進(jìn)行通信,即計(jì)算機(jī)通信基本靠吼。
網(wǎng)絡(luò)層:IP協(xié)議屬于網(wǎng)絡(luò)層的協(xié)議。
如果所有的通信都采用以太網(wǎng)的廣播方式,那么一臺(tái)機(jī)器發(fā)送的包全世界都會(huì)收到,這簡(jiǎn)直難以想象。因此網(wǎng)絡(luò)層就出現(xiàn)了。
世界上的大的網(wǎng)絡(luò)由一個(gè)個(gè)小型的局域網(wǎng)組成,這樣讓以太網(wǎng)的包只能在局域網(wǎng)中傳播可以避免隨意亂吼,但是還必須保證某個(gè)以太網(wǎng)設(shè)備可以連接另外一個(gè)局域網(wǎng)的設(shè)備。因此,必須找出一種方法來(lái)區(qū)分哪些計(jì)算機(jī)屬于同一廣播域,哪些不是,如果是就采用廣播的方式發(fā)送,如果不是,就采用路由的方式(向不同廣播域/子網(wǎng)分發(fā)數(shù)據(jù)包)。
網(wǎng)絡(luò)層功能:引入一套新的地址用來(lái)區(qū)分不同的廣播域/子網(wǎng),這套地址即網(wǎng)絡(luò)地址。
IP協(xié)議:
規(guī)定網(wǎng)絡(luò)地址的協(xié)議叫IP協(xié)議,它定義的地址稱(chēng)之為IP地址,廣泛采用的v4版本即IPv4,它規(guī)定網(wǎng)絡(luò)地址由32位2進(jìn)制表示(現(xiàn)在IPv4早已經(jīng)不夠用啦,IPv6已經(jīng)出來(lái)了)。
范圍0.0.0.0-255.255.255.255
一個(gè)IP地址通常寫(xiě)成四段十進(jìn)制數(shù),例:172.16.10.1
ip地址分成兩部分
網(wǎng)絡(luò)部分:標(biāo)識(shí)子網(wǎng)
主機(jī)部分:標(biāo)識(shí)主機(jī)
但是注意:?jiǎn)渭兊腎P地址段只是標(biāo)識(shí)了IP地址的種類(lèi),從網(wǎng)絡(luò)部分或主機(jī)部分都無(wú)法辨識(shí)一個(gè)IP所處的子網(wǎng),但是可以利用子網(wǎng)掩碼來(lái)解決這個(gè)問(wèn)題。
例:172.16.10.1與172.16.10.2并不能確定二者處于同一子網(wǎng)
所謂”子網(wǎng)掩碼”,就是表示子網(wǎng)絡(luò)特征的一個(gè)參數(shù)。它在形式上等同于IP地址,也是一個(gè)32位二進(jìn)制數(shù)字,它的網(wǎng)絡(luò)部分全部為1,主機(jī)部分全部為0。比如,IP地址172.16.10.1,如果已知網(wǎng)絡(luò)部分是前24位,主機(jī)部分是后8位,那么子網(wǎng)絡(luò)掩碼就是11111111.11111111.11111111.00000000,寫(xiě)成十進(jìn)制就是255.255.255.0。
知道”子網(wǎng)掩碼”,我們就能判斷,任意兩個(gè)IP地址是否處在同一個(gè)子網(wǎng)絡(luò)。方法是將兩個(gè)IP地址與子網(wǎng)掩碼分別進(jìn)行AND運(yùn)算(兩個(gè)數(shù)位都為1,運(yùn)算結(jié)果為1,否則為0),然后比較結(jié)果是否相同,如果是的話,就表明它們?cè)谕粋€(gè)子網(wǎng)絡(luò)中,否則就不是。
子網(wǎng)掩碼的知識(shí)不需要深入了解,我們只需要知道:
IP協(xié)議的作用主要有兩個(gè),一個(gè)是為每一臺(tái)計(jì)算機(jī)分配IP地址,另一個(gè)是確定哪些地址在同一個(gè)子網(wǎng)絡(luò)。
IP數(shù)據(jù)包也分為head和data部分,無(wú)須為IP包定義單獨(dú)的欄位,直接放入以太網(wǎng)包的data部分。
?
以太網(wǎng) headIP headIP data
?
傳輸層:TCP協(xié)議屬于傳輸層的協(xié)議。
以太網(wǎng)層的mac幫我們找到主機(jī),網(wǎng)絡(luò)層的IP幫我們區(qū)分子網(wǎng),這樣基本就能定位到是網(wǎng)絡(luò)上的哪個(gè)設(shè)備了。但是大家在一個(gè)設(shè)備上使用了很多的應(yīng)用程序,你的電腦上可能同時(shí)開(kāi)啟qq,chrome,穿越火線等多個(gè)應(yīng)用程序,如何讓通信直接聯(lián)絡(luò)到某個(gè)應(yīng)用程序呢?答案就是端口,端口即應(yīng)用程序與網(wǎng)卡關(guān)聯(lián)的編號(hào)。
傳輸層功能:建立端口到端口的通信。端口范圍0-65535,但0-1023為系統(tǒng)占用端口,不能給普通應(yīng)用程序使用。
TCP協(xié)議:
TCP協(xié)議是可靠的傳輸,TCP數(shù)據(jù)包的長(zhǎng)度一般不會(huì)超過(guò)IP數(shù)據(jù)包的長(zhǎng)度,以確保單個(gè)TCP數(shù)據(jù)包不必再分割。
?
以太網(wǎng) headIP headTCP headTCP data
?
UDP協(xié)議:
UDP協(xié)議是不可靠傳輸,”報(bào)頭”部分一共只有8個(gè)字節(jié),總長(zhǎng)度不超過(guò)65,535字節(jié),正好放進(jìn)一個(gè)IP數(shù)據(jù)包。
?
以太網(wǎng) headIP headUDP head(8Bytes)UDP data
?
TCP和UDP協(xié)議到底有何優(yōu)劣
UDP提供簡(jiǎn)單的通信機(jī)制,傳輸過(guò)程中丟包或者包內(nèi)容順序錯(cuò)亂也無(wú)法檢測(cè)重發(fā)。但是UDP協(xié)議的頭部信息更少,可以承載更多的數(shù)據(jù)。同時(shí)能夠避免TCP的三次握手,達(dá)到更高的傳輸效率,適用于DNS通信解析和音視頻流媒體傳輸。
TCP相比UDP可以保證可靠的傳輸,能夠檢測(cè)包的完整性,同時(shí)它是有連接的協(xié)議(需要確保對(duì)方可以通信時(shí)才發(fā)送數(shù)據(jù))。
TCP通過(guò)三次握手來(lái)建立連接(重點(diǎn))
所謂TCP三次握手是指建立一個(gè) TCP 連接時(shí)需要客戶(hù)端和服務(wù)器端總共發(fā)送三個(gè)包以確認(rèn)連接的建立。在socket編程中,這一過(guò)程由客戶(hù)端執(zhí)行connect來(lái)觸發(fā)。

第一次握手:客戶(hù)端將標(biāo)志位SYN置為1,隨機(jī)產(chǎn)生一個(gè)值seq=J,并將該數(shù)據(jù)包發(fā)送給服務(wù)器端,客戶(hù)端進(jìn)入SYN_SENT狀態(tài),等待服務(wù)器端確認(rèn)。
第二次握手:服務(wù)器端收到數(shù)據(jù)包后由標(biāo)志位SYN=1知道客戶(hù)端請(qǐng)求建立連接,服務(wù)器端將標(biāo)志位SYN和ACK都置為1,ack=J+1,隨機(jī)產(chǎn)生一個(gè)值seq=K,并將該數(shù)據(jù)包發(fā)送給客戶(hù)端以確認(rèn)連接請(qǐng)求,服務(wù)器端進(jìn)入SYN_RCVD狀態(tài)。
第三次握手:客戶(hù)端收到確認(rèn)后,檢查ack是否為J+1,ACK是否為1,如果正確則將標(biāo)志位ACK置為1,ack=K+1,并將該數(shù)據(jù)包發(fā)送給服務(wù)器端,服務(wù)器端檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,客戶(hù)端和服務(wù)器端進(jìn)入ESTABLISHED狀態(tài),完成三次握手,隨后客戶(hù)端與服務(wù)器端之間可以開(kāi)始傳輸數(shù)據(jù)了。
TCP通過(guò)四次握手來(lái)終止連接(重點(diǎn))
斷開(kāi)一個(gè)TCP連接時(shí),需要客戶(hù)端和服務(wù)端總共發(fā)送4個(gè)包以確認(rèn)連接的斷開(kāi)。在socket編程中,這一過(guò)程由客戶(hù)端或服務(wù)端任一方執(zhí)行close來(lái)觸發(fā)。
由于TCP連接是全雙工的,因此,每個(gè)方向都必須要單獨(dú)進(jìn)行關(guān)閉,這一原則是當(dāng)一方完成數(shù)據(jù)發(fā)送任務(wù)后,發(fā)送一個(gè)FIN來(lái)終止這一方向的連接,收到一個(gè)FIN只是意味著這一方向上沒(méi)有數(shù)據(jù)流動(dòng)了,即不會(huì)再收到數(shù)據(jù)了,但是在這個(gè)TCP連接上仍然能夠發(fā)送數(shù)據(jù),直到這一方向也發(fā)送了FIN。首先進(jìn)行關(guān)閉的一方將執(zhí)行主動(dòng)關(guān)閉,而另一方則執(zhí)行被動(dòng)關(guān)閉。

? ? ? 中斷連接端可以是客戶(hù)端,也可以是服務(wù)器端。
第一次揮手:客戶(hù)端發(fā)送一個(gè)FIN=M,用來(lái)關(guān)閉客戶(hù)端到服務(wù)器端的數(shù)據(jù)傳送,客戶(hù)端進(jìn)入FIN_WAIT_1狀態(tài)。意思是說(shuō)"我客戶(hù)端沒(méi)有數(shù)據(jù)要發(fā)給你了",但是如果你服務(wù)器端還有數(shù)據(jù)沒(méi)有發(fā)送完成,則不必急著關(guān)閉連接,可以繼續(xù)發(fā)送數(shù)據(jù)。
第二次揮手:服務(wù)器端收到FIN后,先發(fā)送ack=M+1,告訴客戶(hù)端,你的請(qǐng)求我收到了,但是我還沒(méi)準(zhǔn)備好,請(qǐng)繼續(xù)你等我的消息。這個(gè)時(shí)候客戶(hù)端就進(jìn)入FIN_WAIT_2 狀態(tài),繼續(xù)等待服務(wù)器端的FIN報(bào)文。
第三次揮手:當(dāng)服務(wù)器端確定數(shù)據(jù)已發(fā)送完成,則向客戶(hù)端發(fā)送FIN=N報(bào)文,告訴客戶(hù)端,好了,我這邊數(shù)據(jù)發(fā)完了,準(zhǔn)備好關(guān)閉連接了。服務(wù)器端進(jìn)入LAST_ACK狀態(tài)。
第四次揮手:客戶(hù)端收到FIN=N報(bào)文后,就知道可以關(guān)閉連接了,但是他還是不相信網(wǎng)絡(luò),怕服務(wù)器端不知道要關(guān)閉,所以發(fā)送ack=N+1后進(jìn)入TIME_WAIT狀態(tài),如果Server端沒(méi)有收到ACK則可以重傳。服務(wù)器端收到ACK后,就知道可以斷開(kāi)連接了??蛻?hù)端等待了2MSL后依然沒(méi)有收到回復(fù),則證明服務(wù)器端已正常關(guān)閉,那好,我客戶(hù)端也可以關(guān)閉連接了。最終完成了四次握手。
應(yīng)用層協(xié)議:HTTP、HTTPS協(xié)議是應(yīng)用層協(xié)議。
用戶(hù)使用的應(yīng)用程序均工作于應(yīng)用層,但大家都可以開(kāi)發(fā)自己的應(yīng)用程序,而且數(shù)據(jù)多種多樣,必須規(guī)定好數(shù)據(jù)的組織形式才能保證可以解析數(shù)據(jù)。
應(yīng)用層功能:規(guī)定應(yīng)用程序的數(shù)據(jù)格式。
HTTP協(xié)議也叫超文本傳輸協(xié)議,是一個(gè)基于請(qǐng)求與響應(yīng)模式的、無(wú)狀態(tài)的、應(yīng)用層的協(xié)議。瀏覽器地址欄輸入的URL,大部分都是基于HTTP或HTTPS協(xié)議的網(wǎng)址。
HTTP協(xié)議的組成
HTTP協(xié)議分請(qǐng)求和響應(yīng)兩部分規(guī)范:
請(qǐng)求:請(qǐng)求行(請(qǐng)求方法+URI+客戶(hù)端協(xié)議版本)+消息報(bào)頭+請(qǐng)求數(shù)據(jù)。
響應(yīng):狀態(tài)行(服務(wù)器協(xié)議版本+狀態(tài)碼+狀態(tài)碼文本描述)+消息報(bào)頭+響應(yīng)數(shù)據(jù)。
HTTP是一個(gè)無(wú)狀態(tài)的協(xié)議,無(wú)狀態(tài)是指協(xié)議對(duì)于事務(wù)處理沒(méi)有記憶能力,服務(wù)器不知道客戶(hù)端是什么狀態(tài)。即我們給服務(wù)器發(fā)送 HTTP 請(qǐng)求之后,服務(wù)器根據(jù)請(qǐng)求,會(huì)給我們發(fā)送數(shù)據(jù)過(guò)來(lái),但是,發(fā)送完,不會(huì)記錄任何信息。這意味著每個(gè)請(qǐng)求都是獨(dú)立的,后續(xù)請(qǐng)求無(wú)法依賴(lài)上一次請(qǐng)求來(lái)判斷身份,后續(xù)我們會(huì)學(xué)到如何解決HTTP無(wú)狀態(tài)的問(wèn)題。
1.0協(xié)議:
服務(wù)器需要處理同時(shí)面向全世界數(shù)十萬(wàn)、上百萬(wàn)客戶(hù)端的網(wǎng)頁(yè)訪問(wèn),但每個(gè)客戶(hù)端與服務(wù)器之間交換數(shù)據(jù)的間歇性較大(即傳輸具有突發(fā)性、瞬時(shí)性),并且網(wǎng)頁(yè)瀏覽的聯(lián)想性、發(fā)散性導(dǎo)致兩次傳送的數(shù)據(jù)關(guān)聯(lián)性很低,大部分通道實(shí)際上會(huì)很空閑、無(wú)端占用資源。因此 HTTP 的設(shè)計(jì)者有意利用這種特點(diǎn)將協(xié)議設(shè)計(jì)為請(qǐng)求時(shí)建連接、請(qǐng)求完釋放連接,以盡快將資源釋放出來(lái)服務(wù)其他客戶(hù)端。
2.0協(xié)議:
隨著時(shí)間的推移,網(wǎng)頁(yè)變得越來(lái)越復(fù)雜,里面可能嵌入了很多圖片,這時(shí)候每次訪問(wèn)圖片都需要建立一次 TCP 連接就顯得很低效。后來(lái),Keep-Alive 被提出用來(lái)解決這效率低的問(wèn)題。
Keep-Alive 功能使客戶(hù)端到服務(wù)器端的連接持續(xù)有效,當(dāng)出現(xiàn)對(duì)服務(wù)器的后繼請(qǐng)求時(shí),Keep-Alive 功能避免了建立或者重新建立連接。
HTTPS協(xié)議
HTTPS協(xié)議是在HTTPS協(xié)議的基礎(chǔ)上對(duì)報(bào)文進(jìn)行加密的協(xié)議,因此HTTPS協(xié)議更安全。
HTTPS協(xié)議需要到CA申請(qǐng)證書(shū)。CA(數(shù)字證書(shū)認(rèn)證,Certificate Authority)機(jī)構(gòu),是承擔(dān)公鑰合法性檢驗(yàn)的第三方權(quán)威機(jī)構(gòu),負(fù)責(zé)指定政策、步驟來(lái)驗(yàn)證用戶(hù)的身份,并對(duì)SSL證書(shū)進(jìn)行簽名,確保證書(shū)持有者的身份和公鑰的所有權(quán)。
SSL證書(shū)可加密敏感信息使其不被泄露,還可以提供身份驗(yàn)證,防止釣魚(yú)網(wǎng)站,SSL證書(shū)是由受信任的CA機(jī)構(gòu)頒發(fā)的,申請(qǐng)證書(shū)時(shí)會(huì)嚴(yán)格的驗(yàn)證企業(yè)/組織的信息,可以消除瀏覽器“不安全”提示。最后SSL證書(shū)還可以提高SEO關(guān)鍵詞排名,谷歌、百度等瀏覽器,明確表示對(duì)安裝SSL證書(shū)的網(wǎng)站進(jìn)行優(yōu)先暫時(shí)排名的權(quán)利。
SSL證書(shū)的頒發(fā)和HTTPS訪問(wèn)流程:
用戶(hù)產(chǎn)生自己的密鑰對(duì),把公鑰和身份信息發(fā)送給CA認(rèn)證中心。
CA核實(shí)之后把數(shù)字證書(shū)發(fā)送給用戶(hù),該證書(shū)包含用戶(hù)的個(gè)人信息和公鑰,附帶認(rèn)證中心的私鑰簽名。
用戶(hù)把認(rèn)證證書(shū)放在網(wǎng)站下,通過(guò)HTTPS提供服務(wù)。
瀏覽器內(nèi)置了各大權(quán)威CA機(jī)構(gòu)根證書(shū),也就包含了CA的公鑰,和用戶(hù)網(wǎng)站進(jìn)行公鑰私鑰比對(duì)就能建立HTTPS/SSL連接。
4.2.3 瀏覽器請(qǐng)求接收流程
打開(kāi)Chrome瀏覽器,地址欄輸入了網(wǎng)址:https://www.google.com的執(zhí)行流程:
通過(guò)DNS進(jìn)行域名解析,我們?cè)L問(wèn)的是一個(gè)域名https://www.google.com,但是網(wǎng)絡(luò)設(shè)備只能通過(guò)ip地址才能找到對(duì)方的主機(jī)。因此首先需要域名解析:
Chrome先從自身DNS緩存中找,但瀏覽器只能緩存1000條。
如果瀏覽器找不到,會(huì)查看操作系統(tǒng)的DNS緩存。
如果操作系統(tǒng)也沒(méi)有找到,則會(huì)根據(jù)本機(jī)配置的首選DNS服務(wù)器發(fā)起域名解析請(qǐng)求(UDP協(xié)議的DNS 53端口)。
DNS運(yùn)營(yíng)商首先從自身查找解析,找不到就迭代的去世界根DNS服務(wù)器查找(世界有13臺(tái)根DNS服務(wù)器)。
最后找到了域名對(duì)應(yīng)的IP地址,則開(kāi)始下一步動(dòng)作。
瀏覽器根據(jù)IP,遵循網(wǎng)絡(luò)協(xié)議找到對(duì)方主機(jī),開(kāi)始建立TCP的3次握手。
瀏覽器會(huì)以隨機(jī)端口向服務(wù)器發(fā)送TCP請(qǐng)求,這中間要經(jīng)過(guò)各種網(wǎng)絡(luò)設(shè)備,甚至還要經(jīng)過(guò)層層防火墻。
建立TCP連接后開(kāi)始發(fā)送HTTP請(qǐng)求,服務(wù)器響應(yīng)HTTP請(qǐng)求,生成HTML頁(yè)面代碼作為響應(yīng)體數(shù)據(jù)發(fā)送給瀏覽器。
Chrome瀏覽器利用Webkit內(nèi)核解析HTML和CSS ,利用V8內(nèi)核解析JavaScript,請(qǐng)附帶請(qǐng)求其他資源。
瀏覽器解析的結(jié)果會(huì)生成一個(gè)DOM樹(shù)和CSS規(guī)則樹(shù),組合構(gòu)造成一個(gè)渲染樹(shù)。
渲染樹(shù)并不等同于 DOM 樹(shù),渲染樹(shù)只會(huì)包括需要顯示的節(jié)點(diǎn)和這些節(jié)點(diǎn)的樣式信息。
CSS 規(guī)則樹(shù) 主要是為了完成匹配并把 CSS規(guī)則 附加到渲染樹(shù)上的每個(gè) 元素上面。
當(dāng)瀏覽器生成渲染樹(shù)以后,就會(huì)根據(jù)渲染樹(shù)來(lái)進(jìn)行布局(也可以叫做回流)。這一階段瀏覽器要做的事情是要弄清楚各個(gè)節(jié)點(diǎn)在頁(yè)面中的確切位置和大小。通常這一行為也被稱(chēng)為“自動(dòng)重排”。
最后通過(guò)調(diào)用操作系統(tǒng) Native GUI 的 API 繪制。
4.2.4 API接口和RESTFul規(guī)范
1. 耦合架構(gòu)和前后端分離
在Vue和React框架出現(xiàn)之前,前端頁(yè)面的開(kāi)發(fā)都是由后端開(kāi)發(fā)人員來(lái)完成,大部分頁(yè)面都是通過(guò)后端的模板渲染引擎(例如JSP、PHP、Django)來(lái)完成的,直到目前為止,依然有很多網(wǎng)站采用這樣的開(kāi)發(fā)方式。
這種開(kāi)發(fā)方式是采用耦合的架構(gòu),所謂耦合,也就是前后端開(kāi)發(fā)沒(méi)有分離,而是把數(shù)據(jù)和UI拼湊在了一起。采用這種方式的開(kāi)發(fā)具有以下缺點(diǎn):
后端開(kāi)發(fā)人員既要負(fù)責(zé)前端還要負(fù)責(zé)后端
如果數(shù)據(jù)結(jié)構(gòu)變化,需要修改模板以適應(yīng)數(shù)據(jù)變化
不專(zhuān)業(yè)的開(kāi)發(fā)人員很容易編寫(xiě)大量頁(yè)面造成項(xiàng)目無(wú)法維護(hù)
頁(yè)面的js文件編寫(xiě)隨意,依賴(lài)重復(fù)加載或忽略加載
因此,隨著技術(shù)的演進(jìn),前端和后端逐漸分離,形成了當(dāng)前大前端的趨勢(shì)。前后端的數(shù)據(jù)交互,完全通過(guò)ajax來(lái)實(shí)現(xiàn),傳輸數(shù)據(jù)約定的規(guī)則就是采用RESTFul API。
采用這種方式開(kāi)發(fā)可以讓前端開(kāi)發(fā)人員只負(fù)責(zé)交互和渲染,后端開(kāi)發(fā)人員只負(fù)責(zé)數(shù)據(jù)維護(hù)和生成,能夠有效進(jìn)行分工,這種開(kāi)發(fā)模式一般稱(chēng)為前后端分離。
2. API接口
API接口(Application Programming Interface,應(yīng)用程序接口)本身就是HTTP(S)協(xié)議的一部分,它約定客戶(hù)端和服務(wù)器數(shù)據(jù)傳輸格式的形式。
下面就是一個(gè)API接口,但是僅僅只定義了一個(gè)地址,卻沒(méi)有提供功能說(shuō)明和其他部分。
https://www.npmjs.com/search?q=express
當(dāng)前的軟件開(kāi)發(fā)領(lǐng)域,API接口的定義規(guī)范主流的規(guī)則被稱(chēng)為RESTFul(Representational State Transfer,表述性狀態(tài)轉(zhuǎn)移)規(guī)范,了解了RESTFul規(guī)范就能熟悉整個(gè)API接口的規(guī)范了。
3. RESTFul規(guī)范
RESTFul規(guī)范大體遵循以下幾點(diǎn):
標(biāo)準(zhǔn)接口URL寫(xiě)法:協(xié)議://域名[:端口]/版本/路徑?過(guò)濾查詢(xún),URL結(jié)尾不應(yīng)該包含斜杠“/”,路徑中首選小寫(xiě)字母,路徑名詞均為復(fù)數(shù),端口號(hào)如果沒(méi)有輸入,表示80的默認(rèn)端口。
如:
查詢(xún)分頁(yè)數(shù)據(jù),https://api.itcast.com/v1/books?page=0&size=10
查詢(xún)單條數(shù)據(jù):https://api.itcast.com/v1/books/id
對(duì)接口的操作,如獲取、新建、更新、刪除,需要在接口方法中指定,而不是在路徑中提供動(dòng)詞。
如:GET?https://api.itcast.com/v1/books?pageNum=0&pageSize=10
狀態(tài)碼:服務(wù)器返回的信息,成功與否必須通過(guò)狀態(tài)碼來(lái)確定,而不是放到response的body內(nèi)部。如果狀態(tài)碼錯(cuò)誤,那么response的body中需要包含一個(gè)error字段來(lái)展示錯(cuò)誤信息。
返回的結(jié)果:
GET返回?cái)?shù)組或單個(gè)資源對(duì)象。
POST返回新生成的對(duì)象。
PUT、PATCH返回修改的對(duì)象
DELETE返回空對(duì)象。
4. 請(qǐng)求和響應(yīng)的Header
Header是HTTP的消息頭部,位于HTTP消息報(bào)頭部分。雖然它不在RESTFul規(guī)范之內(nèi),但是它里面的信息非常重要,Header是由一堆鍵值對(duì)組成的配置信息。由于HTTP協(xié)議分為請(qǐng)求和響應(yīng)兩部分,對(duì)于每個(gè)動(dòng)作,Header里面的配置會(huì)有一些區(qū)別。
Header也可以添加自定義鍵值對(duì),需要鍵名以X-開(kāi)頭,如X-Access-Token:xxx,一般默認(rèn)的瀏覽器定義的Header無(wú)法手動(dòng)覆蓋和修改。
常用的HTTP請(qǐng)求Header
協(xié)議頭說(shuō)明示例狀態(tài)
Accept可接受的響應(yīng)內(nèi)容類(lèi)型(Content-Types)。Accept: text/plain固定
Accept-Charset可接受的字符集Accept-Charset: utf-8固定
Accept-Encoding可接受的響應(yīng)內(nèi)容的編碼方式。Accept-Encoding: gzip, deflate固定
Accept-Language可接受的響應(yīng)內(nèi)容語(yǔ)言列表。Accept-Language: en-US固定
Accept-Datetime可接受的按照時(shí)間來(lái)表示的響應(yīng)內(nèi)容版本Accept-Datetime: Sat, 26 Dec 2015 17:30:00 GMT臨時(shí)
Authorization用于表示HTTP協(xié)議中需要認(rèn)證資源的認(rèn)證信息Authorization: Basic OSdjJGRpbjpvcGVuIANlc2SdDE==固定
Cache-Control用來(lái)指定當(dāng)前的請(qǐng)求/回復(fù)中的,是否使用緩存機(jī)制。Cache-Control: no-cache固定
Connection客戶(hù)端(瀏覽器)想要優(yōu)先使用的連接類(lèi)型Connection: keep-alive固定
Cookie由之前服務(wù)器通過(guò)Set-Cookie(見(jiàn)下文)設(shè)置的一個(gè)HTTP協(xié)議CookieCookie: $Version=1; Skin=new;固定:標(biāo)準(zhǔn)
Content-Length以8進(jìn)制表示的請(qǐng)求體的長(zhǎng)度Content-Length: 348固定
Content-MD5請(qǐng)求體的內(nèi)容的二進(jìn)制 MD5 散列值(數(shù)字簽名),以 Base64 編碼的結(jié)果Content-MD5: oD8dH2sgSW50ZWdyaIEd9D==廢棄
Content-Type請(qǐng)求體的MIME類(lèi)型 (用于POST和PUT請(qǐng)求中)Content-Type: application/x-www-form-urlencoded固定
Date發(fā)送該消息的日期和時(shí)間(以RFC 7231中定義的"HTTP日期"格式來(lái)發(fā)送)Date: Dec, 26 Dec 2015 17:30:00 GMT固定
Expect表示客戶(hù)端要求服務(wù)器做出特定的行為Expect: 100-continue固定
From發(fā)起此請(qǐng)求的用戶(hù)的郵件地址From: user@itbilu.com固定
Host表示服務(wù)器的域名以及服務(wù)器所監(jiān)聽(tīng)的端口號(hào)。如果所請(qǐng)求的端口是對(duì)應(yīng)的服務(wù)的標(biāo)準(zhǔn)端口(80),則端口號(hào)可以省略。Host: www.itbilu.com:8080固定
If-Match僅當(dāng)客戶(hù)端提供的實(shí)體與服務(wù)器上對(duì)應(yīng)的實(shí)體相匹配時(shí),才進(jìn)行對(duì)應(yīng)的操作。主要用于像 PUT 這樣的方法中,僅當(dāng)從用戶(hù)上次更新某個(gè)資源后,該資源未被修改的情況下,才更新該資源。If-Match: "9jd00cdj34pss9ejqiw39d82f20d0ikd"固定
If-Modified-Since允許在對(duì)應(yīng)的資源未被修改的情況下返回304未修改If-Modified-Since: Dec, 26 Dec 2015 17:30:00 GMT固定
If-None-Match允許在對(duì)應(yīng)的內(nèi)容未被修改的情況下返回304未修改( 304 Not Modified ),參考 超文本傳輸協(xié)議 的實(shí)體標(biāo)記If-None-Match: "9jd00cdj34pss9ejqiw39d82f20d0ikd"固定
If-Range如果該實(shí)體未被修改過(guò),則向返回所缺少的那一個(gè)或多個(gè)部分。否則,返回整個(gè)新的實(shí)體If-Range: "9jd00cdj34pss9ejqiw39d82f20d0ikd"固定
If-Unmodified-Since僅當(dāng)該實(shí)體自某個(gè)特定時(shí)間以來(lái)未被修改的情況下,才發(fā)送回應(yīng)。If-Unmodified-Since: Dec, 26 Dec 2015 17:30:00 GMT固定
Max-Forwards限制該消息可被代理及網(wǎng)關(guān)轉(zhuǎn)發(fā)的次數(shù)。Max-Forwards: 10固定
Origin發(fā)起一個(gè)針對(duì)跨域資源共享的請(qǐng)求(該請(qǐng)求要求服務(wù)器在響應(yīng)中加入一個(gè)Access-Control-Allow-Origin的消息頭,表示訪問(wèn)控制所允許的來(lái)源)。Origin: http://www.itbilu.com固定: 標(biāo)準(zhǔn)
Pragma與具體的實(shí)現(xiàn)相關(guān),這些字段可能在請(qǐng)求/回應(yīng)鏈中的任何時(shí)候產(chǎn)生。Pragma: no-cache固定
Proxy-Authorization用于向代理進(jìn)行認(rèn)證的認(rèn)證信息。Proxy-Authorization: Basic IOoDZRgDOi0vcGVuIHNlNidJi2==固定
Range表示請(qǐng)求某個(gè)實(shí)體的一部分,字節(jié)偏移以0開(kāi)始。Range: bytes=500-999固定
Referer表示瀏覽器所訪問(wèn)的前一個(gè)頁(yè)面,可以認(rèn)為是之前訪問(wèn)頁(yè)面的鏈接將瀏覽器帶到了當(dāng)前頁(yè)面。Referer其實(shí)是Referrer這個(gè)單詞,但RFC制作標(biāo)準(zhǔn)時(shí)給拼錯(cuò)了,后來(lái)也就將錯(cuò)就錯(cuò)使用Referer了。Referer:?http://itbilu.com/nodejs固定
TE瀏覽器預(yù)期接受的傳輸時(shí)的編碼方式:可使用回應(yīng)協(xié)議頭Transfer-Encoding中的值(還可以使用"trailers"表示數(shù)據(jù)傳輸時(shí)的分塊方式)用來(lái)表示瀏覽器希望在最后一個(gè)大小為0的塊之后還接收到一些額外的字段。TE: trailers,deflate固定
User-Agent瀏覽器的身份標(biāo)識(shí)字符串User-Agent: Mozilla/……固定
Upgrade要求服務(wù)器升級(jí)到一個(gè)高版本協(xié)議。Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11固定
Via告訴服務(wù)器,這個(gè)請(qǐng)求是由哪些代理發(fā)出的。Via: 1.0 fred, 1.1 itbilu.com.com (Apache/1.1)固定
Warning一個(gè)一般性的警告,表示在實(shí)體內(nèi)容體中可能存在錯(cuò)誤。Warning: 199 Miscellaneous warning固定
常用的HTTP響應(yīng)頭
響應(yīng)頭說(shuō)明示例狀態(tài)
Access-Control-Allow-Origin指定哪些網(wǎng)站可以跨域源資源共享Access-Control-Allow-Origin: *臨時(shí)
Accept-Patch指定服務(wù)器所支持的文檔補(bǔ)丁格式Accept-Patch: text/example;charset=utf-8固定
Accept-Ranges服務(wù)器所支持的內(nèi)容范圍Accept-Ranges: bytes固定
Age響應(yīng)對(duì)象在代理緩存中存在的時(shí)間,以秒為單位Age: 12固定
Allow對(duì)于特定資源的有效動(dòng)作;Allow: GET, HEAD固定
Cache-Control通知從服務(wù)器到客戶(hù)端內(nèi)的所有緩存機(jī)制,表示它們是否可以緩存這個(gè)對(duì)象及緩存有效時(shí)間。其單位為秒Cache-Control: max-age=3600固定
Connection針對(duì)該連接所預(yù)期的選項(xiàng)Connection: close固定
Content-Disposition對(duì)已知MIME類(lèi)型資源的描述,瀏覽器可以根據(jù)這個(gè)響應(yīng)頭決定是對(duì)返回資源的動(dòng)作,如:將其下載或是打開(kāi)。Content-Disposition: attachment; filename="fname.ext"固定
Content-Encoding響應(yīng)資源所使用的編碼類(lèi)型。Content-Encoding: gzip固定
Content-Language響就內(nèi)容所使用的語(yǔ)言Content-Language: zh-cn固定
Content-Length響應(yīng)消息體的長(zhǎng)度,用8進(jìn)制字節(jié)表示Content-Length: 348固定
Content-Location所返回的數(shù)據(jù)的一個(gè)候選位置Content-Location: /index.htm固定
Content-MD5響應(yīng)內(nèi)容的二進(jìn)制 MD5 散列值,以 Base64 方式編碼Content-MD5: IDK0iSsgSW50ZWd0DiJUi==已淘汰
Content-Range如果是響應(yīng)部分消息,表示屬于完整消息的哪個(gè)部分Content-Range: bytes 21010-47021/47022固定
Content-Type當(dāng)前內(nèi)容的MIME類(lèi)型Content-Type: text/html; charset=utf-8固定
Date此條消息被發(fā)送時(shí)的日期和時(shí)間(以RFC 7231中定義的"HTTP日期"格式來(lái)表示)Date: Tue, 15 Nov 1994 08:12:31 GMT固定
ETag對(duì)于某個(gè)資源的某個(gè)特定版本的一個(gè)標(biāo)識(shí)符,通常是一個(gè) 消息散列ETag: "737060cd8c284d8af7ad3082f209582d"固定
Expires指定一個(gè)日期/時(shí)間,超過(guò)該時(shí)間則認(rèn)為此回應(yīng)已經(jīng)過(guò)期Expires: Thu, 01 Dec 1994 16:00:00 GMT固定: 標(biāo)準(zhǔn)
Last-Modified所請(qǐng)求的對(duì)象的最后修改日期(按照 RFC 7231 中定義的“超文本傳輸協(xié)議日期”格式來(lái)表示)Last-Modified: Dec, 26 Dec 2015 17:30:00 GMT固定
Link用來(lái)表示與另一個(gè)資源之間的類(lèi)型關(guān)系,此類(lèi)型關(guān)系是在RFC 5988中定義Link:; rel="alternate"固定
Location用于在進(jìn)行重定向,或在創(chuàng)建了某個(gè)新資源時(shí)使用。Location:?http://www.itbilu.com/nodejs固定
P3PP3P策略相關(guān)設(shè)置P3P: CP="This is not a P3P policy!固定
Pragma與具體的實(shí)現(xiàn)相關(guān),這些響應(yīng)頭可能在請(qǐng)求/回應(yīng)鏈中的不同時(shí)候產(chǎn)生不同的效果Pragma: no-cache固定
Proxy-Authenticate要求在訪問(wèn)代理時(shí)提供身份認(rèn)證信息。Proxy-Authenticate: Basic固定
Public-Key-Pins用于防止中間攻擊,聲明網(wǎng)站認(rèn)證中傳輸層安全協(xié)議的證書(shū)散列值Public-Key-Pins: max-age=2592000; pin-sha256="……";固定
Refresh用于重定向,或者當(dāng)一個(gè)新的資源被創(chuàng)建時(shí)。默認(rèn)會(huì)在5秒后刷新重定向。Refresh: 5; url=http://itbilu.com
Retry-After如果某個(gè)實(shí)體臨時(shí)不可用,那么此協(xié)議頭用于告知客戶(hù)端稍后重試。其值可以是一個(gè)特定的時(shí)間段(以秒為單位)或一個(gè)超文本傳輸協(xié)議日期。示例1:Retry-After: 120示例2: Retry-After: Dec, 26 Dec 2015 17:30:00 GMT固定
Server服務(wù)器的名稱(chēng)Server: nginx/1.6.3固定
Set-Cookie設(shè)置HTTP cookieSet-Cookie: UserID=itbilu; Max-Age=3600; Version=1固定: 標(biāo)準(zhǔn)
Status通用網(wǎng)關(guān)接口的響應(yīng)頭字段,用來(lái)說(shuō)明當(dāng)前HTTP連接的響應(yīng)狀態(tài)。Status: 200 OK
TrailerTrailer用戶(hù)說(shuō)明傳輸中分塊編碼的編碼信息Trailer: Max-Forwards固定
Transfer-Encoding用表示實(shí)體傳輸給用戶(hù)的編碼形式。包括:chunked、compress、 deflate、gzip、identity。Transfer-Encoding: chunked固定
Upgrade要求客戶(hù)端升級(jí)到另一個(gè)高版本協(xié)議。Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11固定
Vary告知下游的代理服務(wù)器,應(yīng)當(dāng)如何對(duì)以后的請(qǐng)求協(xié)議頭進(jìn)行匹配,以決定是否可使用已緩存的響應(yīng)內(nèi)容而不是重新從原服務(wù)器請(qǐng)求新的內(nèi)容。Vary: *固定
Via告知代理服務(wù)器的客戶(hù)端,當(dāng)前響應(yīng)是通過(guò)什么途徑發(fā)送的。Via: 1.0 fred, 1.1 itbilu.com (nginx/1.6.3)固定
Warning一般性警告,告知在實(shí)體內(nèi)容體中可能存在錯(cuò)誤。Warning: 199 Miscellaneous warning固定
WWW-Authenticate表示在請(qǐng)求獲取這個(gè)實(shí)體時(shí)應(yīng)當(dāng)使用的認(rèn)證模式。WWW-Authenticate: Basic固定
Header中的Content-Type
Content-Type表示請(qǐng)求或響應(yīng)的數(shù)據(jù)類(lèi)型。
其格式是type/subType;charset;boundary
type:subType:主類(lèi)型/次類(lèi)型,如text/html,application/json。
charset,字符編碼標(biāo)準(zhǔn),一般是utf-8。
boundary,用于分隔請(qǐng)求主體的字段分隔符。
常用的用于請(qǐng)求的有如下幾個(gè)
application/x-www-form-urlencoded: 數(shù)據(jù)被編碼成以 '&' 分隔的鍵-值對(duì), 同時(shí)以 '=' 分隔鍵和值. 非字母或數(shù)字的字符會(huì)被轉(zhuǎn)碼,不要用于文件等二進(jìn)制數(shù)據(jù)。在POST中會(huì)放到body中,在GET中會(huì)追加到URL后面。express的body-parser通過(guò)urlencoded方法來(lái)解析。
application/x-www-form-urlencoded請(qǐng)求的主體內(nèi)容
POST / HTTP/1.1
Content-Type:application/x-www-form-urlencoded//指定了類(lèi)型
Accept-Encoding: gzip, deflate
Host: w.sohu.com
Content-Length: 21//content內(nèi)容長(zhǎng)度
Connection: Keep-Alive
Cache-Control: no-cache// 下面是一條空行隔開(kāi)的內(nèi)容,然后是被格式化的請(qǐng)求參數(shù)。
txt1=hello&txt2=world
multipart/form-data:在application/x-www-form-urlencoded的基礎(chǔ)上支持文件上傳,但body中的內(nèi)容會(huì)更多,所以如果沒(méi)有文件,不建議用這個(gè)。
boundary用來(lái)告訴服務(wù)器從哪里分隔主體內(nèi)容。
multipart/form-data請(qǐng)求的主體內(nèi)容
POST /t2/upload.do HTTP/1.1
User-Agent: SOHUWapRebot
Accept-Language: zh-cn,zh;q=0.5
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Content-Length: 60408
// boundary用來(lái)告訴服務(wù)器從哪里分隔主體內(nèi)容
// multipart/form-data的數(shù)據(jù)主體很明顯是把每一個(gè)字段都做了細(xì)致的描述,如每個(gè)字段還可以包含Content-Disposition,Content-Type,Content-Transfer-Encoding屬性描述,來(lái)告訴服務(wù)器如何解析這個(gè)字段,因此會(huì)顯著增大傳輸?shù)膬?nèi)容容量。
Content-Type:multipart/form-data; boundary=ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Host: w.sohu.com
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data; name="city"
Santa colo
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="desc"
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
...
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="pic"; filename="photo.jpg"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
... binary data of the jpg ...
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC--
application/json:支持序列化的json(JSON.stringify),序列化的json可以存儲(chǔ)更復(fù)雜的鍵值對(duì)嵌套結(jié)構(gòu),包括數(shù)組、Date等的原生轉(zhuǎn)換。
POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"title":"test","sub":[1,2,3]}
text/plain:傳遞的數(shù)據(jù)就是純文本,服務(wù)器不做任何處理。
text/html:傳遞的數(shù)據(jù)會(huì)被解析成html。
5. 接口測(cè)試工具
Postman
Postman是一個(gè)非常好用的API調(diào)試工具,通過(guò)這個(gè)工具可以任意指定和查看HTTP請(qǐng)求的Header(包括Content-Type)、方法以及請(qǐng)求數(shù)據(jù),而且能夠?qū)?shù)據(jù)進(jìn)行格式化。在測(cè)試服務(wù)器接口的時(shí)候,這個(gè)工具能夠讓我們快速的預(yù)覽和調(diào)試接口。

Chrome -> 開(kāi)發(fā)者工具 -> NetWork
除了使用第三方工具,瀏覽器一般在開(kāi)發(fā)者工具中也集成了接口調(diào)試,在其中可以查看接口請(qǐng)求的所有信息。

Swagger
Swagger是一個(gè)API接口文檔生成工具,除了能夠根據(jù)annotation生成API文檔之外,自動(dòng)生成的文檔中還可以測(cè)試每個(gè)接口。
安裝
npm install --save swagger-ui-express
編寫(xiě)一個(gè)符合swagger規(guī)范的API文檔,規(guī)范格式參考swagger editor。命名為swagger.json,放到工程根目錄下。
{
? ? "swagger": "2.0",
? ? "info": {
? ? ? ? "description": "這是一個(gè)測(cè)試的API文檔,你可以使用**Markdown語(yǔ)法**",
? ? ? ? "version": "1.0.0",
? ? ? ? "title": "API文檔",
? ? ? ? "contact": {
? ? ? ? ? ? "email": "apiteam@swagger.io"
? ? ? ? }
? ? },
? ? "host": "localhost:3000",
? ? "basePath": "/v1",
? ? "tags": [
? ? ? ? {
? ? ? ? ? ? "name": "user",
? ? ? ? ? ? "description": "用戶(hù)操作",
? ? ? ? ? ? "externalDocs": {
? ? ? ? ? ? ? ? "description": "更多信息請(qǐng)見(jiàn)",
? ? ? ? ? ? ? ? "url": "http://swagger.io"
? ? ? ? ? ? }
? ? ? ? }
? ? ],
? ? "schemes": [
? ? ? ? "https",
? ? ? ? "http"
? ? ],
? ? "paths": {
? ? ? ? "/user": {
? ? ? ? ? ? "post": {
? ? ? ? ? ? ? ? "tags": [
? ? ? ? ? ? ? ? ? ? "user"
? ? ? ? ? ? ? ? ],
? ? ? ? ? ? ? ? "summary": "添加用戶(hù)",
? ? ? ? ? ? ? ? "description": "",
? ? ? ? ? ? ? ? "operationId": "addPet",
? ? ? ? ? ? ? ? "consumes": [
? ? ? ? ? ? ? ? ? ? "application/json",
? ? ? ? ? ? ? ? ? ? "application/xml"
? ? ? ? ? ? ? ? ],
? ? ? ? ? ? ? ? "produces": [
? ? ? ? ? ? ? ? ? ? "application/xml",
? ? ? ? ? ? ? ? ? ? "application/json"
? ? ? ? ? ? ? ? ],
? ? ? ? ? ? ? ? "parameters": [
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? "in": "body",
? ? ? ? ? ? ? ? ? ? ? ? "name": "name",
? ? ? ? ? ? ? ? ? ? ? ? "description": "用戶(hù)的名字",
? ? ? ? ? ? ? ? ? ? ? ? "required": true
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ],
? ? ? ? ? ? ? ? "responses": {
? ? ? ? ? ? ? ? ? ? "405": {
? ? ? ? ? ? ? ? ? ? ? ? "description": "非法輸入"
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
修改index.js代碼
const express = require('express')
const swaggerUi = require('swagger-ui-express')
const app = express()
const swaggerDocument = require('./swagger.json');
// 訪問(wèn)api地址可以查看api
app.use('/api', swaggerUi.serve, swaggerUi.setup(swaggerDocument))
app.listen(3000, () => {
? ? console.log('服務(wù)器創(chuàng)建完畢,監(jiān)聽(tīng)3000端口')
})
執(zhí)行index.js
node index.js
瀏覽器打開(kāi)http://localhost:3000/api?即可查看效果。

點(diǎn)擊user一欄,會(huì)展開(kāi),點(diǎn)擊Try it out即可測(cè)試接口數(shù)據(jù)。
4.3 Express常用中間件
了解了服務(wù)器和客戶(hù)端的通信原理,接下來(lái)我們就可以使用Express來(lái)編寫(xiě)自己Web應(yīng)用的接口了。Express編寫(xiě)業(yè)務(wù)邏輯大部分都是基于中間件去實(shí)現(xiàn)的,中間件封裝了一些特定的邏輯,輔助我們可以快速的開(kāi)發(fā)。
4.3.1 什么是中間件
Express 是一個(gè)路由和中間件 Web 框架,其自身只具有最低程度的功能:Express 應(yīng)用程序基本上是一系列中間件函數(shù)調(diào)用。
Express 應(yīng)用程序可以使用以下類(lèi)型的中間件: