
構(gòu)建Web應用
前后端采用的語言都是JavaScript,在跨越HTTP進行溝通時的好處;
無須切換語言環(huán)境,部分知識不會因為語言環(huán)境的切換而丟失,會有一些額外的好處;
數(shù)據(jù)(因為JSON)可以很好的實現(xiàn)跨前后端直接使用;
一些業(yè)務(如模板渲染)可以很自由地輕量地選擇前端還是后端進行,因為編程語言相同,所以切換代價??;
1. 基礎(chǔ)功能
對于一個Web應用而言,在具體的業(yè)務上,可能存在的需求有:
請求方法的判斷;
URL的路徑解析;
URL中查詢字符串解析;
Cookie的解析;
Basic認證
表單數(shù)據(jù)的解析
任意格式文件的上傳處理;
Session(會話);
1.1 請求方法
請求方法包括:
GET;
POST;
HEAD;
DELETE;
PUT;
CONNECT;
1.2 路徑解析
客戶端代理(瀏覽器)會將完整的URL地址解析成報文,將路徑和查詢部分放在報文第一行。
最常見的根據(jù)路徑進行業(yè)務處理的應用是靜態(tài)文件服務器,會根據(jù)路徑去查找磁盤中的文件,然后將其響應給客戶端。
還有一種常見的分發(fā)場景是根據(jù)路徑來選擇控制器,預設(shè)路徑為控制器和行為組合,無須額外配置路由信息。
1.3 查詢字符串
字符串會跟隨在路徑后,形成請求報文首行的第二部分。
在業(yè)務調(diào)用產(chǎn)生之前,中間件或者框架會將查詢字符串轉(zhuǎn)換,然后掛載在對象上供業(yè)務使用。
1.4 Cookie
-
初識Cookie
Cookie的處理分為如下幾步:
服務器向客戶端發(fā)送Cookie;
瀏覽器將Cookie保存;
之后每次瀏覽器都會將Cookie發(fā)向服務器端;
可選參數(shù)會影響瀏覽器在后續(xù)發(fā)送給服務器端的行為。主要為以下幾個選項:
path;
Expires和Max-Age
HttpOnly
Secure
-
Cookie的性能影響
針對Cookie設(shè)置過多,造成的帶寬的浪費的性能優(yōu)化方案:
-
減少Cookie的大小
如果在域名的根節(jié)點設(shè)置Cookie,幾乎所有子路徑下的請求都會帶上這些Cookie。
-
為靜態(tài)組件使用不同的域名
為不需要Cookie的組件換個域名可以實現(xiàn)減少無效Cookie的傳輸,還可以突破瀏覽器下載線程的限制,但是將會增加域名轉(zhuǎn)換為IP的DNS查詢。
-
減少DNS查詢
減少DNS查詢和使用不同的域名看似沖突,但使用DNS緩存會削弱這個副作用。
-
1.5 Session
通過Cookie,瀏覽器和服務器可以實現(xiàn)狀態(tài)的記錄。但Cookie并非完美,缺點是:體積過大;Cookie在前后端均可以被修改,數(shù)據(jù)極易被篡改和偽造。綜上,Cookie對于敏感數(shù)據(jù)的保護是無效的。
為了解決Cookie敏感數(shù)據(jù)的問題,Session應運而生。
Session的數(shù)據(jù)只保留在服務端,客戶端無法修改,數(shù)據(jù)的安全有一定的保障,數(shù)據(jù)也無需在協(xié)議中每次傳輸。
如何將每個客戶的服務器的數(shù)據(jù)一一對應:
-
基于Cookie來實現(xiàn)用戶和數(shù)據(jù)的映射;
雖然將所有的數(shù)據(jù)都放在Cookie中不可取,但是將口令放在Cookie中是可以的。口令一旦被篡改,就丟失了映射關(guān)系,也無法修改服務器端存在的數(shù)據(jù)了。
Session的有效期通常較短,普遍的設(shè)置是20分鐘,如果20分鐘之內(nèi)服務器端和客戶端沒有交互產(chǎn)生,服務器端將數(shù)據(jù)刪除。由于數(shù)據(jù)過期時間較短,且在服務器端存儲數(shù)據(jù),因此安全性相對較高。
口令是如何產(chǎn)生的?
一旦服務器端啟用了Session,它將約定一個鍵值作為Session的口令,這個值可以隨意約定。一旦服務器端檢查到用戶請求Cookie沒有攜帶該值,它就會為之生成一個值,這個值是唯一且不重復的值,并設(shè)定超時時間。
這種方案依賴Cookie的實現(xiàn)。
如果客戶端禁止使用Cookie,這個世界大多數(shù)的網(wǎng)站將無法實現(xiàn)登錄等操作。
-
通過查詢字符串來實現(xiàn)瀏覽器端和服務器端數(shù)據(jù)的對應;
它的原理是檢查請求的查詢字符串,如果沒有值,會先生成新的帶值的URL。
利用HTTP請求頭中的ETag;
-
Session與內(nèi)存
為了解決性能問題和Session數(shù)據(jù)無法跨進程共享的問題,常用的方案是將Session集中化,將原本分散在多進程里的數(shù)據(jù),統(tǒng)一轉(zhuǎn)移到集中的數(shù)據(jù)存儲中。目前常用的工具是Redis、MEmcached等,通過這些高效的緩存,Node進程無須再內(nèi)部維護數(shù)據(jù)對象,垃圾回收問題和內(nèi)存限制問題都可以迎刃而解。
采用第三方緩存來存儲Session會引起的一個問題時引起網(wǎng)絡(luò)訪問。
理論上來說,訪問網(wǎng)絡(luò)中的數(shù)據(jù)比訪問本地磁盤中的數(shù)據(jù)要慢,涉及到握手、傳輸、網(wǎng)絡(luò)終端自身的磁盤I/O等。
采用第三方高速緩存的理由:
Node與緩存服務保持長連接,而非頻繁的短連接,握手導致的延遲只影響初始化;
高速緩存直接在內(nèi)存中進行數(shù)據(jù)存儲和訪問;
緩存服務通常與Node進程會比在相同的機器上或者相同的機房里,網(wǎng)絡(luò)速度受到的影響較小;
-
Session與安全
Session的安全主要指如何讓這個口令更加安全。
一種做法是將這個口令通過私鑰加密進行簽名,使得偽造的成本較高。
一種方案是將客戶端的某些獨有信息與口令作為原值,然后簽名,這樣攻擊者一旦不在原始的客戶端進行訪問,就會導致簽名失敗。這些獨有信息包括用戶IP和用戶代理(User Agent)。
-
XSS漏洞
XSS的全稱是跨站腳本攻擊(Cross Site Scripting),通常都是網(wǎng)站開發(fā)者決定哪些腳本可以執(zhí)行在瀏覽器端。
XSS主要形成的原因多數(shù)是用戶的輸入沒有被轉(zhuǎn)義,而被直接執(zhí)行。
1.6 緩存
在HTTP之上構(gòu)建的應用,其客戶端除了比普通桌面應用具備更輕量的升級和部署等特性外,在跨平臺、跨瀏覽器、跨設(shè)備上也具有獨特的優(yōu)勢。
傳統(tǒng)客戶端在安裝后的應用過程中僅僅需要傳輸數(shù)據(jù),Web應用還需要傳輸構(gòu)成界面的組件(HTML、CSS、JavaScript)。
為了提高性能,關(guān)于緩存的規(guī)則:
添加Expires或Cache-Control到報文頭中;
配置ETags;
讓Ajax可緩存;
通常來說,POST、DELETE、PUT這類帶行為性的請求操作一般不做任何緩存,大多數(shù)緩存只應用在GET請求中。
簡單來說,本地沒有文件時,瀏覽器會請求服務端的內(nèi)容,并將這部分內(nèi)容放置在本地的某個緩存目錄中。第二次請求時,它將對本地文件進行檢查,如果不能確定這份本地文件是否可以直接使用,它將發(fā)起一次請求。
所謂條件請求,就是在普通的GET請求報文中,附帶If-Modified-Since字段。
它將詢問服務器是否有更新的版本,本地文件的最后修改時間。如果服務器沒有新的版本,只需響應一個304狀態(tài)碼,客戶端就使用本地版本。如果服務器端有新的版本,就將新的內(nèi)容發(fā)送給客戶端,客戶端放棄本地版本。
條件請求采用時間戳的方式實現(xiàn)的缺陷:
文件的時間戳改定但內(nèi)容并不一定改定;
時間戳只能精確到秒級別,更新頻繁的內(nèi)容將無法生效;
HTTP1.1中引入ETag解決采用時間戳的缺陷。
ETag的全稱是Entity Tag,由服務器端生成,服務器端可以決定它的生成規(guī)則。如果根據(jù)文件內(nèi)容生成散列值,那么條件請求將不會受到時間戳改動造成的帶寬浪費。
盡管條件請求可以在文件內(nèi)容沒有修改的情況下節(jié)省帶寬,但是依然會發(fā)起一個HTTP請求,使得客戶端依然會花一定時間來等待響應。最好的方案就是連條件請求都不用發(fā)起,在服務器端響應內(nèi)容時,讓瀏覽器明確地將內(nèi)存緩存起來。在響應里設(shè)置Expires或Cache-Control頭,瀏覽器將根據(jù)改值進行緩存。
Expires是一個GMT格式的時間字符串。
瀏覽器在接到這個過期值后,只有本地還存在這個緩存文件,在到期時間之前我它都不會發(fā)起請求。但是Expires的缺席在于瀏覽器和服務器之間的時間可能不一致,可能導致文件提前過期,或者到期后文件并沒有被刪除。
Cache-Control能夠避免瀏覽器端與服務器端時間不同步帶來的不一致問題,只要進行類似倒計時方式計算過期時間即可。Cache-Control的值還能設(shè)置public、private、no-cache、no-store等能夠更精細地控制緩存的選項。
在瀏覽器中,Expires和Cache-Control同時存在的話,且被同時支持時,max-age會覆蓋Expires。
-
清除緩存
設(shè)置緩存可以達到節(jié)省帶寬的目的,但是緩存一旦設(shè)定,當服務器端意外更新內(nèi)容時,卻無法通知客戶端更新。這使得在使用緩存時,也要為其設(shè)定版本號,所幸瀏覽器是根據(jù)URL進行緩存。
一般的更新機制有:
每次發(fā)布,路徑中跟隨Web應用的版本號:http://love.bleaknight.top/?v=20200110
每次發(fā)布,路徑中跟隨該文件內(nèi)容的hash值:http://love.bleaknight.top/?hash=bleaknight
1.7 Basic認證
Basic認證是當客戶端與服務器端進行請求時,允許通過用戶名和密碼實現(xiàn)的一種身份認證方式。
Basic認證有太多的缺點,雖然經(jīng)過Base64加密后在網(wǎng)絡(luò)中傳輸,但是這近乎明文,十分的危險,一般只有在HTTPS的情況才會使用。
2. 數(shù)據(jù)上傳
在業(yè)務中,需要接受的一些數(shù)據(jù)包括:表單提交、文件提交、JSON上傳、XML上傳等。
2.1 表單數(shù)據(jù)
最常見的數(shù)據(jù)提交就是通過網(wǎng)頁表單提交數(shù)據(jù)到服務器端。
2.2 其他格式
除了表單數(shù)據(jù)外,常見的提交還有JSON和XML文件等,都是依據(jù)Content-Type中的值決定,其中JSON類型的值為appliction/json,XML的值為application/xml。
2.3 附件上傳
除了常見的表單和特殊格式的內(nèi)容提交,還有一種比較獨特的表單,特殊表單與普通表單的差異在于該表單中可以含有file類型的控件,以及需要指定表單屬性enctype為multipart/form-data。
2.4 數(shù)據(jù)上傳與安全
內(nèi)存和CSRF相關(guān)的安全問題。
-
內(nèi)存限制
在解析表單、JSON和XML部分,采用的策略是先保存用戶提交的所有數(shù)據(jù),然后在解析處理,最后才傳遞給業(yè)務邏輯。
這種策略存在潛在的問題時,它僅僅適合數(shù)據(jù)量小的提交請求,一旦數(shù)據(jù)量過大,將發(fā)生內(nèi)存被占光的情況。
解決此問題的方案:
限制上傳內(nèi)容的大小,一旦超過限制,停止接受數(shù)據(jù),并響應400狀態(tài)碼;
通過流式解析,將數(shù)據(jù)流導向磁盤中,Node只保留文件路徑等小數(shù)據(jù);
-
CSRF
CSRF的全稱是Cross-SIte Request Forgery(跨站請求偽造)。
3. 路由解析
3.1文件路徑型
靜態(tài)文件
-
動態(tài)文件
在MVC模式流行起來之前,根據(jù)文件路徑執(zhí)行動態(tài)腳本也是基本的路由方式,處理原理是Web服務器根據(jù)URL路徑找到對應的文件,如/index.asp或index.php。Web服務器根據(jù)文件名后綴去 尋找腳本的解釋器,并傳入HTTP請求的上下文。
解析器執(zhí)行腳本,并輸出響應報文,達到完成服務的目的。
3.2 MVC
MVC模型的主要思想是將業(yè)務邏輯按職責分類,主要分為:
模型(Model),數(shù)據(jù)相關(guān)的操作和封裝;
視圖(View),視圖的渲染;
控制器(Controller),一組行為的集合;
最經(jīng)典的分層模式:
[圖片上傳失敗...(image-a3866d-1578987281362)]
分層模式的工作模式的說明:
路由解析,根據(jù)URL尋找到對應的控制器和行為;
行為調(diào)用相關(guān)的模型,進行數(shù)據(jù)操作;
數(shù)據(jù)操作結(jié)束后,調(diào)用視圖和相關(guān)數(shù)據(jù)進行頁面渲染,輸出到客戶端;
如何根據(jù)URL做路由映射?
通過手工關(guān)聯(lián)映射,會有一個對應的路由文件來將URL映射到對應的控制器;
通過自然關(guān)聯(lián)映射,沒有對應的路由文件;
-
手工映射
手工映射除了需要手工配置路由外較為原始外,它對URL的要求十分靈活,幾乎沒有格式上的限制。
正則匹配
參數(shù)解析
自然映射
3.3 RESTful
RESTful的全稱是Representational State Transfer(表現(xiàn)層狀態(tài)轉(zhuǎn)化)。
符合RESTful規(guī)范的設(shè)計,成為RESTful設(shè)計。其設(shè)計哲學主要將服務器提供的內(nèi)容實體看作一個資源,并表現(xiàn)在URL上。
4. 中間件
中間件(middleware)來簡化和隔離這些基礎(chǔ)設(shè)施與業(yè)務邏輯之間的細節(jié),讓開發(fā)者能夠關(guān)注在 業(yè)務的開發(fā)上,已達到提升開發(fā)效率的目的。
中間價的行為比較類似于Java過濾器(filter)的工作原理,就是在進入具體的業(yè)務處理之前,先讓過濾器處理。
4.1 異常處理
使用next()方法添加err參數(shù),并捕獲中間件直接拋出的同步異常。
4.2 中間件與性能
中間件性能提升的點:
編寫高效的中間件
合理利用路由,避免不必要的中間件執(zhí)行
-
編寫高效的中間件
優(yōu)化的方法:
使用高效的方法。
緩存需要重復計算的結(jié)果。
避免不必要的計算。
-
合理使用路由
合理的路由使得不必要的中間件不參與請求處理的過程。
5.頁面渲染
5.1 內(nèi)容響應
服務端響應的報文,最終都要被終端(命令行終端、代碼終端、瀏覽器)處理。服務器端的響應從一定程度上決定或指示了客戶端該如何處理響應的內(nèi)容。
內(nèi)容響應的過程中,響應報頭中的Content-*字段十分重要。
-
MIME
瀏覽器通過不同的Content-Type的值決定采用不同的渲染方式,這個值簡稱為MIME。
-
附件下載
在一些場景下,無論響應的內(nèi)容是怎樣的MIME值,需求中并不要求客戶端去打開它,只需要彈出并下載它即可。為了滿足這種需求,Content-Disposition字段應聲登場。Content-Disposition字段影響的行為是客戶端會根據(jù)它 的值是應該將報文數(shù)據(jù)當做即時瀏覽的內(nèi)容,還是可下載的附件。當內(nèi)容只需即時查看時,它的值為inline,當數(shù)據(jù)可以存為附件時,它的值為attachment。
響應JSON
響應跳轉(zhuǎn)
5.2 視圖渲染
主流的普通的HTML內(nèi)容的響應總稱為視圖渲染。
在動態(tài)頁面技術(shù)中,最終的視圖是由模板和數(shù)據(jù)共同生成出來的。
模板是帶有特殊標簽的HTML片段,通過與數(shù)據(jù)的渲染,將數(shù)據(jù)填充到這些特殊標簽中,最終生成普通的帶數(shù)據(jù)的HTML片段。通常將這種渲染方法設(shè)計為render(),參數(shù)就是模板路徑和數(shù)據(jù)。
5.3 模板
模板技術(shù)雖然多種多樣,但它的實質(zhì)就是將模板文件和數(shù)據(jù)通過模板引擎生成最終的HTML代碼。
形成模板技術(shù)的4個要素:
模板語言
包含模板語言的模板文件
擁有動態(tài)數(shù)據(jù)的數(shù)據(jù)對象
模板引擎
模板和數(shù)據(jù)與最終結(jié)果相比,這里有一個靜態(tài)、動態(tài)的劃分過程,相同的模板和不同的數(shù)據(jù)可以得到不然的結(jié)果,不同的模板與相同的數(shù)據(jù)也能得到不同的結(jié)果。模板技術(shù)使得網(wǎng)頁中的動態(tài)內(nèi)容和靜態(tài)內(nèi)容變得不相互依賴,數(shù)據(jù)開發(fā)者與模板開發(fā)者只要約定好數(shù)據(jù)結(jié)構(gòu),兩者就不用相互影響了。
模板技術(shù)實際上就是拼接字符串這樣很底層的活,只是各種模板有著各自的優(yōu)缺點和技巧。
-
模板引擎
使用render()方法實現(xiàn)一個簡單的模板引擎。
語法分解;
處理表達式;
生成待執(zhí)行的語句;
與數(shù)據(jù)一起執(zhí)行,生成最終字符串;
模板編譯
為了能夠最終與數(shù)據(jù)一起執(zhí)行生成字符串,需要將原始的模板字符串轉(zhuǎn)換成一個函數(shù)對象。這個過程稱為模板編譯,生成的中間函數(shù)只與模板字符串相關(guān),與具體的數(shù)據(jù)無關(guān)。如果每次都生成這個中間函數(shù),就會浪費CPU。為了提升模板渲染的性能速度,通常會采用模板預編譯的方式。
通過預編譯緩存模板編譯后的結(jié)果,實際應用中就可以實現(xiàn)一次編譯,多次執(zhí)行,而原始的方式每次執(zhí)行過程中都要進行一次編譯和執(zhí)行。
-
with的應用
-
模板安全
在使用模板技術(shù)的時候,與輸入有關(guān)的變量一定要轉(zhuǎn)義。
-
模板邏輯
集成文件系統(tǒng)
-
字模板
模板文件太大,太多復雜,會增加維護上的難度。有些模板是可以重用的,這催生了字模板(Partial View)的產(chǎn)生。字模板可以嵌套在別的模板中,多個模板可以嵌入同一個字模板中。
-
布局視圖
字模板主要商務為了重用模板和降低模板的復雜度。字模板的另一種使用方式就是布局視圖(layout),布局視圖又稱母版頁,它與字模板的原理相同,但是場景稍有區(qū)別。
-
緩存性能
模板引擎的優(yōu)化步驟:
緩存模板文件
緩存模板文件編譯后的函數(shù)
優(yōu)化模板中的執(zhí)行表達式
-
小結(jié)
模板技術(shù)的出現(xiàn),將業(yè)務開發(fā)與HTML輸出的工作分離開,它的設(shè)計原理就是單一職責原理。
5.4 Bigpipe
Bigpipe產(chǎn)生于Facebook公司的前端加載技術(shù),它的提出主要是為了解決重數(shù)據(jù)頁面的加載速度問題。
最終的HTML要在所有的數(shù)據(jù)獲取完成后才輸出到瀏覽器端。Node通過異步已經(jīng)將多個數(shù)據(jù)源的獲取并行起來了,最終的頁面輸出速度取決于兩個數(shù)據(jù)請求中響應時間慢的那個。
BigPipe的解決思路是將頁面分割成多個部分(pagelet),先向用戶輸出沒有數(shù)據(jù)的布局(框架),將每個部分逐步輸出到前端,再最終渲染填充框架,完成整個網(wǎng)頁的渲染。
Bigpipe是一個需要前后端配合實現(xiàn)的優(yōu)化技術(shù):
頁面布局框架(無數(shù)據(jù)的);
后端持續(xù)性的數(shù)據(jù)輸出;
前端渲染;
-
頁面布局框架
頁面布局架構(gòu)由后端渲染而出。
-
持續(xù)數(shù)據(jù)輸出
模板輸出后,整個網(wǎng)頁的渲染并沒有結(jié)束,但用戶已經(jīng)可以看到整個頁面的大體樣子。
前端渲染
-
小結(jié)
Bigpipe將網(wǎng)頁布局和數(shù)據(jù)渲染分離,使得用戶在視覺上覺得網(wǎng)頁提前渲染好了,其隨著數(shù)據(jù)輸出的過程逐步渲染頁面,使得用戶能夠感知頁面是活的。