深刻淺出Node.js學習筆記(八)

構建Web應用

先後端採用的語言都是JavaScript,在跨越HTTP進行溝通時的好處;php

  • 無須切換語言環境,部分知識不會由於語言環境的切換而丟失,會有一些額外的好處;
  • 數據(由於JSON)能夠很好的實現跨先後端直接使用;
  • 一些業務(如模板渲染)能夠很自由地輕量地選擇前端仍是後端進行,由於編程語言相同,因此切換代價小;

1. 基礎功能

對於一個Web應用而言,在具體的業務上,可能存在的需求有:前端

  • 請求方法的判斷;
  • URL的路徑解析;
  • URL中查詢字符串解析;
  • Cookie的解析;
  • Basic認證
  • 表單數據的解析
  • 任意格式文件的上傳處理;
  • Session(會話);

1.1 請求方法

請求方法包括:編程

  1. GET;
  2. POST;
  3. HEAD;
  4. DELETE;
  5. PUT;
  6. CONNECT;

1.2 路徑解析

客戶端代理(瀏覽器)會將完整的URL地址解析成報文,將路徑和查詢部分放在報文第一行。json

最多見的根據路徑進行業務處理的應用是靜態文件服務器,會根據路徑去查找磁盤中的文件,而後將其響應給客戶端。後端

還有一種常見的分發場景是根據路徑來選擇控制器,預設路徑爲控制器和行爲組合,無須額外配置路由信息。瀏覽器

1.3 查詢字符串

字符串會跟隨在路徑後,造成請求報文首行的第二部分。緩存

在業務調用產生以前,中間件或者框架會將查詢字符串轉換,而後掛載在對象上供業務使用。安全

1.4 Cookie

  1. 初識Cookie性能優化

    Cookie的處理分爲以下幾步:服務器

    • 服務器向客戶端發送Cookie;
    • 瀏覽器將Cookie保存;
    • 以後每次瀏覽器都會將Cookie發向服務器端;

    可選參數會影響瀏覽器在後續發送給服務器端的行爲。主要爲如下幾個選項:

    • path;
    • Expires和Max-Age
    • HttpOnly
    • Secure
  2. Cookie的性能影響

    針對Cookie設置過多,形成的帶寬的浪費的性能優化方案:

    • 減小Cookie的大小

      若是在域名的根節點設置Cookie,幾乎全部子路徑下的請求都會帶上這些Cookie。

    • 爲靜態組件使用不一樣的域名

      爲不須要Cookie的組件換個域名能夠實現減小無效Cookie的傳輸,還能夠突破瀏覽器下載線程的限制,可是將會增長域名轉換爲IP的DNS查詢。

    • 減小DNS查詢

      減小DNS查詢和使用不一樣的域名看似衝突,但使用DNS緩存會削弱這個反作用。

1.5 Session

經過Cookie,瀏覽器和服務器能夠實現狀態的記錄。但Cookie並不是完美,缺點是:體積過大;Cookie在先後端都可以被修改,數據極易被篡改和僞造。綜上,Cookie對於敏感數據的保護是無效的。

爲了解決Cookie敏感數據的問題,Session應運而生。

Session的數據只保留在服務端,客戶端沒法修改,數據的安全有必定的保障,數據也無需在協議中每次傳輸。

如何將每一個客戶的服務器的數據一一對應:

  1. 基於Cookie來實現用戶和數據的映射;

    雖然將全部的數據都放在Cookie中不可取,可是將口令放在Cookie中是能夠的。口令一旦被篡改,就丟失了映射關係,也沒法修改服務器端存在的數據了。

    Session的有效期一般較短,廣泛的設置是20分鐘,若是20分鐘以內服務器端和客戶端沒有交互產生,服務器端將數據刪除。因爲數據過時時間較短,且在服務器端存儲數據,所以安全性相對較高。

    口令是如何產生的?

    一旦服務器端啓用了Session,它將約定一個鍵值做爲Session的口令,這個值能夠隨意約定。一旦服務器端檢查到用戶請求Cookie沒有攜帶該值,它就會爲之生成一個值,這個值是惟一且不重複的值,並設定超時時間。

    這種方案依賴Cookie的實現。

    若是客戶端禁止使用Cookie,這個世界大多數的網站將沒法實現登陸等操做。

  2. 經過查詢字符串來實現瀏覽器端和服務器端數據的對應;

    它的原理是檢查請求的查詢字符串,若是沒有值,會先生成新的帶值的URL。

  3. 利用HTTP請求頭中的ETag;

  • Session與內存

    爲了解決性能問題和Session數據沒法跨進程共享的問題,經常使用的方案是將Session集中化,將本來分散在多進程裏的數據,統一轉移到集中的數據存儲中。目前經常使用的工具是Redis、MEmcached等,經過這些高效的緩存,Node進程無須再內部維護數據對象,垃圾回收問題和內存限制問題均可以迎刃而解。

    採用第三方緩存來存儲Session會引發的一個問題時引發網絡訪問。

    理論上來講,訪問網絡中的數據比訪問本地磁盤中的數據要慢,涉及到握手、傳輸、網絡終端自身的磁盤I/O等。

    採用第三方高速緩存的理由:

    • Node與緩存服務保持長鏈接,而非頻繁的短鏈接,握手致使的延遲隻影響初始化;
    • 高速緩存直接在內存中進行數據存儲和訪問;
    • 緩存服務一般與Node進程會比在相同的機器上或者相同的機房裏,網絡速度受到的影響較小;
  • Session與安全

    Session的安全主要指如何讓這個口令更加安全。

    一種作法是將這個口令經過私鑰加密進行簽名,使得僞造的成本較高。

    一種方案是將客戶端的某些獨有信息與口令做爲原值,而後簽名,這樣攻擊者一旦不在原始的客戶端進行訪問,就會致使簽名失敗。這些獨有信息包括用戶IP和用戶代理(User Agent)。

  • XSS漏洞

    XSS的全稱是跨站腳本攻擊(Cross Site Scripting),一般都是網站開發者決定哪些腳本能夠執行在瀏覽器端。

    XSS主要造成的緣由多數是用戶的輸入沒有被轉義,而被直接執行。

1.6 緩存

在HTTP之上構建的應用,其客戶端除了比普通桌面應用具有更輕量的升級和部署等特性外,在跨平臺、跨瀏覽器、跨設備上也具備獨特的優點。

傳統客戶端在安裝後的應用過程當中僅僅須要傳輸數據,Web應用還須要傳輸構成界面的組件(HTML、CSS、JavaScript)。

爲了提升性能,關於緩存的規則:

  • 添加Expires或Cache-Control到報文頭中;
  • 配置ETags;
  • 讓Ajax可緩存;

一般來講,POST、DELETE、PUT這類帶行爲性的請求操做通常不作任何緩存,大多數緩存只應用在GET請求中。

簡單來講,本地沒有文件時,瀏覽器會請求服務端的內容,並將這部份內容放置在本地的某個緩存目錄中。第二次請求時,它將對本地文件進行檢查,若是不能肯定這份本地文件是否能夠直接使用,它將發起一次請求。

所謂條件請求,就是在普通的GET請求報文中,附帶If-Modified-Since字段。

它將詢問服務器是否有更新的版本,本地文件的最後修改時間。若是服務器沒有新的版本,只需響應一個304狀態碼,客戶端就使用本地版本。若是服務器端有新的版本,就將新的內容發送給客戶端,客戶端放棄本地版本。

條件請求採用時間戳的方式實現的缺陷:

  • 文件的時間戳改定但內容並不必定改定;
  • 時間戳只能精確到秒級別,更新頻繁的內容將沒法生效;

HTTP1.1中引入ETag解決採用時間戳的缺陷。

ETag的全稱是Entity Tag,由服務器端生成,服務器端能夠決定它的生成規則。若是根據文件內容生成散列值,那麼條件請求將不會受到時間戳改動形成的帶寬浪費。

儘管條件請求能夠在文件內容沒有修改的狀況下節省帶寬,可是依然會發起一個HTTP請求,使得客戶端依然會花必定時間來等待響應。最好的方案就是連條件請求都不用發起,在服務器端響應內容時,讓瀏覽器明確地將內存緩存起來。在響應裏設置Expires或Cache-Control頭,瀏覽器將根據改值進行緩存。

Expires是一個GMT格式的時間字符串。

瀏覽器在接到這個過時值後,只有本地還存在這個緩存文件,在到期時間以前我它都不會發起請求。可是Expires的缺席在於瀏覽器和服務器之間的時間可能不一致,可能致使文件提早過時,或者到期後文件並無被刪除。

Cache-Control可以避免瀏覽器端與服務器端時間不一樣步帶來的不一致問題,只要進行相似倒計時方式計算過時時間便可。Cache-Control的值還能設置public、private、no-cache、no-store等可以更精細地控制緩存的選項。

在瀏覽器中,Expires和Cache-Control同時存在的話,且被同時支持時,max-age會覆蓋Expires。

  • 清除緩存

    設置緩存能夠達到節省帶寬的目的,可是緩存一旦設定,當服務器端意外更新內容時,卻沒法通知客戶端更新。這使得在使用緩存時,也要爲其設定版本號,所幸瀏覽器是根據URL進行緩存。

    通常的更新機制有:

1.7 Basic認證

Basic認證是當客戶端與服務器端進行請求時,容許經過用戶名和密碼實現的一種身份認證方式。

Basic認證有太多的缺點,雖然通過Base64加密後在網絡中傳輸,可是這近乎明文,十分的危險,通常只有在HTTPS的狀況纔會使用。

2. 數據上傳

在業務中,須要接受的一些數據包括:表單提交、文件提交、JSON上傳、XML上傳等。

2.1 表單數據

最多見的數據提交就是經過網頁表單提交數據到服務器端。

2.2 其餘格式

除了表單數據外,常見的提交還有JSON和XML文件等,都是依據Content-Type中的值決定,其中JSON類型的值爲appliction/json,XML的值爲application/xml。

2.3 附件上傳

除了常見的表單和特殊格式的內容提交,還有一種比較獨特的表單,特殊表單與普通表單的差別在於該表單中能夠含有file類型的控件,以及須要指定表單屬性enctype爲multipart/form-data。

2.4 數據上傳與安全

內存和CSRF相關的安全問題。

  1. 內存限制

    在解析表單、JSON和XML部分,採用的策略是先保存用戶提交的全部數據,而後在解析處理,最後才傳遞給業務邏輯。

    這種策略存在潛在的問題時,它僅僅適合數據量小的提交請求,一旦數據量過大,將發生內存被佔光的狀況。

    解決此問題的方案:

    • 限制上傳內容的大小,一旦超過限制,中止接受數據,並響應400狀態碼;
    • 經過流式解析,將數據流導向磁盤中,Node只保留文件路徑等小數據;
  2. CSRF

    CSRF的全稱是Cross-SIte Request Forgery(跨站請求僞造)。

3. 路由解析

3.1文件路徑型

  1. 靜態文件

  2. 動態文件

    在MVC模式流行起來以前,根據文件路徑執行動態腳本也是基本的路由方式,處理原理是Web服務器根據URL路徑找到對應的文件,如/index.asp或index.php。Web服務器根據文件名後綴去 尋找腳本的解釋器,並傳入HTTP請求的上下文。

    解析器執行腳本,並輸出響應報文,達到完成服務的目的。

3.2 MVC

MVC模型的主要思想是將業務邏輯按職責分類,主要分爲:

  • 模型(Model),數據相關的操做和封裝;
  • 視圖(View),視圖的渲染;
  • 控制器(Controller),一組行爲的集合;

最經典的分層模式:

image-20200110161337011

分層模式的工做模式的說明:

  • 路由解析,根據URL尋找到對應的控制器和行爲;
  • 行爲調用相關的模型,進行數據操做;
  • 數據操做結束後,調用視圖和相關數據進行頁面渲染,輸出到客戶端;

如何根據URL作路由映射?

  1. 經過手工關聯映射,會有一個對應的路由文件來將URL映射到對應的控制器;

  2. 經過天然關聯映射,沒有對應的路由文件;

  3. 手工映射

    手工映射除了須要手工配置路由外較爲原始外,它對URL的要求十分靈活,幾乎沒有格式上的限制。

    • 正則匹配
    • 參數解析
  4. 天然映射

3.3 RESTful

RESTful的全稱是Representational State Transfer(表現層狀態轉化)。

符合RESTful規範的設計,成爲RESTful設計。其設計哲學主要將服務器提供的內容實體看做一個資源,並表如今URL上。

4. 中間件

中間件(middleware)來簡化和隔離這些基礎設施與業務邏輯之間的細節,讓開發者可以關注在 業務的開發上,已達到提高開發效率的目的。

中間價的行爲比較相似於Java過濾器(filter)的工做原理,就是在進入具體的業務處理以前,先讓過濾器處理。

4.1 異常處理

使用next()方法添加err參數,並捕獲中間件直接拋出的同步異常。

4.2 中間件與性能

中間件性能提高的點:

  • 編寫高效的中間件
  • 合理利用路由,避免沒必要要的中間件執行
  1. 編寫高效的中間件

    優化的方法:

    • 使用高效的方法。
    • 緩存須要重複計算的結果。
    • 避免沒必要要的計算。
  2. 合理使用路由

    合理的路由使得沒必要要的中間件不參與請求處理的過程。

5.頁面渲染

5.1 內容響應

服務端響應的報文,最終都要被終端(命令行終端、代碼終端、瀏覽器)處理。服務器端的響應從必定程度上決定或指示了客戶端該如何處理響應的內容。

內容響應的過程當中,響應報頭中的Content-*字段十分重要。

  1. MIME

    瀏覽器經過不一樣的Content-Type的值決定採用不一樣的渲染方式,這個值簡稱爲MIME。

  2. 附件下載

    在一些場景下,不管響應的內容是怎樣的MIME值,需求中並不要求客戶端去打開它,只須要彈出並下載它便可。爲了知足這種需求,Content-Disposition字段應聲登場。Content-Disposition字段影響的行爲是客戶端會根據它 的值是應該將報文數據當作即時瀏覽的內容,仍是可下載的附件。當內容只需即時查看時,它的值爲inline,當數據能夠存爲附件時,它的值爲attachment。

  3. 響應JSON

  4. 響應跳轉

5.2 視圖渲染

主流的普通的HTML內容的響應總稱爲視圖渲染。

在動態頁面技術中,最終的視圖是由模板和數據共同生成出來的。

模板是帶有特殊標籤的HTML片斷,經過與數據的渲染,將數據填充到這些特殊標籤中,最終生成普通的帶數據的HTML片斷。一般將這種渲染方法設計爲render(),參數就是模板路徑和數據。

5.3 模板

模板技術雖然多種多樣,但它的實質就是將模板文件和數據經過模板引擎生成最終的HTML代碼。

造成模板技術的4個要素:

  • 模板語言
  • 包含模板語言的模板文件
  • 擁有動態數據的數據對象
  • 模板引擎

模板和數據與最終結果相比,這裏有一個靜態、動態的劃分過程,相同的模板和不一樣的數據能夠獲得否則的結果,不一樣的模板與相同的數據也能獲得不一樣的結果。模板技術使得網頁中的動態內容和靜態內容變得不相互依賴,數據開發者與模板開發者只要約定好數據結構,二者就不用相互影響了。

模板技術實際上就是拼接字符串這樣很底層的活,只是各類模板有着各自的優缺點和技巧。

  1. 模板引擎

    使用render()方法實現一個簡單的模板引擎。

    • 語法分解;
    • 處理表達式;
    • 生成待執行的語句;
    • 與數據一塊兒執行,生成最終字符串;

    模板編譯

    爲了可以最終與數據一塊兒執行生成字符串,須要將原始的模板字符串轉換成一個函數對象。這個過程稱爲模板編譯,生成的中間函數只與模板字符串相關,與具體的數據無關。若是每次都生成這個中間函數,就會浪費CPU。爲了提高模板渲染的性能速度,一般會採用模板預編譯的方式。

    經過預編譯緩存模板編譯後的結果,實際應用中就能夠實現一次編譯,屢次執行,而原始的方式每次執行過程當中都要進行一次編譯和執行。

  2. with的應用

    • 模板安全

      在使用模板技術的時候,與輸入有關的變量必定要轉義。

  3. 模板邏輯

  4. 集成文件系統

  5. 字模板

    模板文件太大,太多複雜,會增長維護上的難度。有些模板是能夠重用的,這催生了字模板(Partial View)的產生。字模板能夠嵌套在別的模板中,多個模板能夠嵌入同一個字模板中。

  6. 佈局視圖

    字模板主要商務爲了重用模板和下降模板的複雜度。字模板的另外一種使用方式就是佈局視圖(layout),佈局視圖又稱母版頁,它與字模板的原理相同,可是場景稍有區別。

  7. 緩存性能

    模板引擎的優化步驟:

    • 緩存模板文件
    • 緩存模板文件編譯後的函數
    • 優化模板中的執行表達式
  8. 小結

    模板技術的出現,將業務開發與HTML輸出的工做分離開,它的設計原理就是單一職責原理。

5.4 Bigpipe

Bigpipe產生於Facebook公司的前端加載技術,它的提出主要是爲了解決重數據頁面的加載速度問題。

最終的HTML要在全部的數據獲取完成後才輸出到瀏覽器端。Node經過異步已經將多個數據源的獲取並行起來了,最終的頁面輸出速度取決於兩個數據請求中響應時間慢的那個。

BigPipe的解決思路是將頁面分割成多個部分(pagelet),先向用戶輸出沒有數據的佈局(框架),將每一個部分逐步輸出到前端,再最終渲染填充框架,完成整個網頁的渲染。

Bigpipe是一個須要先後端配合實現的優化技術:

  • 頁面佈局框架(無數據的);
  • 後端持續性的數據輸出;
  • 前端渲染;
  1. 頁面佈局框架

    頁面佈局架構由後端渲染而出。

  2. 持續數據輸出

    模板輸出後,整個網頁的渲染並無結束,但用戶已經能夠看到整個頁面的大致樣子。

  3. 前端渲染

  4. 小結

    Bigpipe將網頁佈局和數據渲染分離,使得用戶在視覺上以爲網頁提早渲染好了,其隨着數據輸出的過程逐步渲染頁面,使得用戶可以感知頁面是活的。

相關文章
相關標籤/搜索