web開發中,必須要了解的HTTP相關知識
本文已同步到 github , web開發中,必須要了解的HTTP相關知識 ,歡迎收藏,歡迎start。
本文主要記錄與HTTP相關的具體概念和知識,關於HTTP協議的誕生和歷史發展,不多做介紹,自己但是既然是寫HTTP,順帶說兩句,上下文也能銜接的上。
CERN(歐洲核子研究組織)的蒂姆 • 伯納斯 - 李(Tim BernersLee)博士提出了一種能讓遠隔兩地的研究者們共享知識的設想,於是HTTP慢慢的誕生了。
另外,HTTP協議是無狀態可以,於是為了儲存使用者的狀態,cookie誕生了。
HTTP協議是建立在TCP連線之上的,當瀏覽器輸入URL進行訪問,瀏覽器衝URL中解析出主機名和埠,瀏覽器建立一條與web伺服器的連線,然後才進行http請求。
TCP連線的建立與終止
建立TCP連線(三次握手)
在客戶端與服務端進行http通訊之前,需要建立TCP連線,這時需要三次握手
(1) 請求新的TCP連線,客戶端傳送一個小的TCP分組,這個分組設定一個特殊的SYN標記,表明是一個客戶端請求。
(2) 如果伺服器接受這個連線,就會對一些連線引數進行計算,並向客戶端回送一個TCP分組,傳送SY和ACK標記,表明連線請求已經被接受
(3) 最後,客戶端向伺服器回送一條確認訊息,通知伺服器連線已經建立。
斷開TCP連線(四次斷開)
建立一個連線需要三次握手,而終止一個連線要經過4次握手。這由TCP的半關閉(half-close)造成的。既然一個TCP連線是全雙工(即資料在兩個方向上能同時傳遞),因此每個方
向必須單獨地進行關閉。這原則就是當一方完成它的資料傳送任務後就能傳送一個FIN來終止
這個方向連線。當一端收到一個FIN,它必須通知應用層另一端幾經終止了那個方向的資料傳
送。傳送FIN通常是應用層進行關閉的結果。
(1) 客戶端傳送FIN標記到伺服器,表明客戶端發起關閉連線
(2) 伺服器接收客戶端的FIN標記並,向客戶端傳送FIN的ACK確認標記
(3) 伺服器傳送FIN到客戶端,伺服器關閉連線
(4) 伺服器端傳送一個FIN的ACK確認標記,確認連線關閉
建立持久連線的請求和響應互動:
使用wireshark進行資料抓包:
這裡向大家推薦一款抓包軟體 Wireshark ,可以用來分析TCP連線的建立和斷開過程,以及抓取HTTP請求和相應的資訊等,下面是我進行一次客戶端和服務端通訊的抓包資料截圖:
HTTP報文
HTTP協議報文是應用程式之間傳送的資料塊,也就是客戶端和服務端用於互動的資訊。客戶端的報文叫做請求報文,伺服器端的報文叫做響應報文。
HTTP報文組成
HTTP報文由起始行、首部和實體的主體(也稱報文主體或主體)組成。起始行和首部以一個回車符和換行符作為結束,主體部分可以是二進位制資料,也可以為空。
1. 起始行
請求報文起始行:
請求報文起始行說明了要做什麼,由請求方法 、請求URI和協議版本構成。
GET /index.html HTTP/1.1
響應報文起始行:
響應報文的起始行,由協議版本、狀態碼和原因短語構成。
HTTP/1.1 200 OK// OK就是原因短語
2. 首部
首部欄位分類
-
1.通用首部
客戶端和服務端都可以使用的首部
通用首部欄位表:
-
2.請求首部
請求報文特有的首部,為伺服器提供了一些額外的資訊,補充了請求的附加內容、客戶端資訊、響應內容相關的優先順序等資訊。
請求首部欄位
-
3.響應首部
響應報文特有的欄位
響應首部欄位表:
-
4.實體首部
用於針對請求報文和響應報文主體部分使用的首部
-
5.擴充套件首部
擴充套件首部是非標準的首部,由應用程式開發者建立,但還未新增到已批准的HTTP標準中去。
http狀態碼
狀態碼的職責是當客戶端向伺服器端傳送請求時,描述返回的請求結果。藉助狀態碼,使用者可以知道伺服器端是正常處理了請求,還是出現了錯誤。
狀態碼分類:
狀態碼區間 | 類別 |
---|---|
100~199 | 資訊性狀態碼 |
200~299 | 成功狀態碼 |
300~399 | 重定向狀態碼 |
400~499 | 客戶端錯誤狀態碼 |
500~599 | 伺服器錯誤狀態碼 |
常用狀態碼列表:
狀態碼 | 原因短語 | 含義 |
---|---|---|
200 | OK | 表示從客戶端發來的請求在伺服器端被正常處理了 |
204 | No Content | 該狀態碼代表伺服器接收的請求已成功處理,但在返回的響應報文中不含實體的主體部分。另外,也不允許返回任何實體的主體。 |
301 | Moved Permanently | 永久重定向,該狀態碼錶示請求的資源已被分配了新的 URI,以後應使用資源現在所指的 URI |
302 | Found | 臨時性重定向,該狀態碼錶示請求的資源已被分配了新的 URI,希望使用者(本次)能使用新的 URI 訪問 |
303 | See Other | 303 狀態碼和 302 Found 狀態碼有著相同的功能,但 303 狀態碼明確表示客戶端應當採用 GET 方法獲取資源,這點與 302 狀態碼有區別 |
304 | Not Modified | 快取 |
307 | Temporary Redirect | 臨時重定向,和302一樣 |
400 | Bad Request | 該狀態碼錶示請求報文中存在語法錯誤。當錯誤發生時,需修改請求的內容後再次傳送請求。另外,瀏覽器會像 200 OK 一樣對待該狀態碼 |
401 | Unauthorized | 該狀態碼錶示傳送的請求需要有通過 HTTP 認證(BASIC 認證、DIGEST 認證)的認證資訊 |
403 | Forbidden | 該狀態碼錶明對請求資源的訪問被伺服器拒絕了 |
404 | Not Found | 該狀態碼錶明伺服器上無法找到請求的資源 |
500 | Internal Server Error | 該狀態碼錶明伺服器端在執行請求時發生了錯誤。也有可能是 Web應用存在的 bug 或某些臨時的故障 |
502 | Bad Gateway | 閘道器錯誤 |
503 | Service Unavailable | 該狀態碼錶明伺服器暫時處於超負載或正在進行停機維護,現在無法處理請求。如果事先得知解除以上狀況需要的時間,最好寫入RetryAfter 首部欄位再返回給客戶端 |
HTTP中不同場景下,首部欄位的作用
1. CORS 跨域資源共享
跨域資源共享( CORS ) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓執行在一個 origin (domain) 上的Web應用被准許訪問來自不同源伺服器上的指定的資源。當一個資源從與該資源本身所在的伺服器不同的域、協議或埠請求一個資源時,資源會發起一個跨域 HTTP 請求。 --MDN
下面使用nodejs來搭建一個簡單的伺服器,來介紹一個跨域問題的解決方法
// index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>CORS</title> </head> <body> Hello World <script> fetch('http://127.0.0.1:8081') </script> </body> </html>
// server.js const http = require('http') http.createServer(function(req, res) { res.writeHead('200', { 'Access-Control-Allow-Origin': 'http://localhost:8082' }) }).listen(8081)
在源地址為 http://localhost :8082 下,請求 http://localhost :8081,是跨域請求,瀏覽器會自動在request Header中傳送Origin首部欄位,並把值設定為來自哪個源,本例為 http://localhost :8081。伺服器需要在響應頭中設定Access-Control-Allow-Origin,來告知瀏覽器可以處理返回的資料。如果響應頭中不設定Access-Control-Allow-Origin則會報錯,但是返回狀態碼為200,跨域實際上是瀏覽器本身的一個安全機制。
// server2.js // 啟動8082埠服務,在瀏覽器中訪問http://127.0.0.1:8082,會返回index.html內容 const http = require('http') const fs = require('fs') http.createServer(function(req, res) { var page = fs.readFileSync('index.html', 'utf-8') res.writeHead(200, { 'Content-Type': 'text/html' }) res.end(page) }).listen(8082)
關於CORS跨域請求的分類:
1.簡單請求:
需要同時滿足以下的條件就是簡單請求
(1)請求方法:
GET、POST、HEAD
(2)請求頭不能為以下其他欄位之外
Accept Accept-Language Content-Language Content-Type的值必須為application/x-www-form-urlencoded、multipart/form-data、text/plain之一
2.非簡單請求:
非簡單請求是當請求資訊不滿足簡單請求的條件,瀏覽器就傳送方法為OPTIONS的預請求,包含自己請求的方法及需要使用的請求頭欄位,在得到伺服器響應允許之後,瀏覽器會按照想要使用的請求方法及頭資訊再發一次請求。
現在修改以下上面的例子:
// index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>CORS</title> </head> <body> Hello World <script> fetch('http://127.0.0.1:8081', { method: 'PUT', headers: { X-Coustom-Head: 'abc' } }) </script> </body> </html>
// server.js const http = require('http') http.createServer(function(req, res) { res.writeHead('200', { 'Access-Control-Allow-Origin': 'http://localhost:8082' }) }).listen(8081)
如果服務端不進行相應的設定告訴瀏覽器允許跨域訪問則會報錯
但是預請求返回狀態碼為200
// server2.js // 啟動8082埠服務,在瀏覽器中訪問http://127.0.0.1:8082,會返回index.html內容 const http = require('http') const fs = require('fs') http.createServer(function(req, res) { var page = fs.readFileSync('index.html', 'utf-8') res.writeHead(200, { 'Content-Type': 'text/html' }) res.end(page) }).listen(8082)
現在我們修改以下 server.js
// server.js const http = require('http') http.createServer(function(req, res) { res.writeHead('200', { 'Access-Control-Allow-Origin': 'http://localhost:8082', 'Access-Control-Allow-Headers': 'X-Coustom-Head', 'Access-Control-Allow-Methods': 'PUT' }) }).listen(8081)
重新啟動node服務,訪問 http://locaohost :8082,可以看到在傳送預請求後,瀏覽器會繼續傳送PUT請求
關於CORS的其他設定這裡就不多做介紹了,這裡主要是用一個例子來說明以下http不同欄位在跨域場景下的作用。
2. 快取 (Cache-Control的作用)
本例依舊用node服務來講解一下Cache-Control的作用,新建三個檔案
// index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Cache-Control</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <script src="/script.js"></script> </body> </html>
// script.js console.log('script.js')
// server.js const http = require('http') const fs = require('fs') http.createServer(function(req, res) { if (req.url === '/') { let page = fs.readFileSync('index2.html', 'utf-8') res.writeHead(200, { 'Content-Type': 'text/html' }) res.end(page) } if (req.url === '/script.js') { let page = fs.readFileSync('script.js', 'utf-8') res.writeHead(200, { 'Content-Type': 'text/javascript', 'Cache-Control': 'max-age=10' }) res.end(page) } }).listen(8082)
在第一次請求script.js資源時,向伺服器傳送請求
由於伺服器返回響應時,設定Cache-Control: 'max-age=10'時,修改script.js後,在10秒內繼續請求script.js資源,則從快取中讀取,而列印資訊依舊是'script.js'
// script.js console.log('script-modify.js')
更多關於快取的知識在這裡也不多介紹了,貼兩張cache-control欄位在請求和響應時可以設定的值和其表示含義:
1. Cache-Control 快取請求指令:
2. Cache-Control 快取響應指令:
3. cookie
指某些網站為了辨別使用者身份、進行 session 跟蹤而儲存在使用者本地終端上的資料(通常經過加密),當下次再訪問時瀏覽器會將該網站的cookie發回給伺服器端。
cookie如果不設定過期時間,隨瀏覽器關閉而失效,如果有需要可以設定過期時間,繼續上程式碼例子:chestnut:,新建兩個檔案如下
// index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Cookie</title> </head> <body> Cookie <script> console.log(document.cookie) </script> </body> </html>
// server.js const http = require('http') const fs = require('fs') http.createServer(function(req, res) { if (req.url === '/') { let page = fs.readFileSync('index.html', 'utf-8') res.writeHead(200, { 'Content-Type': 'text/html', 'Set-Cookie': ['a=1;max-age:5', 'b=2;HTTPOnly'] }) res.end(page) } }).listen(8082)
啟動node服務,訪問localhost:8082,可以看到成功設定了cookie
並在響應頭資訊中設定了Set-Cookie欄位
另外關注以下列印資訊,發現只有a=1,因為給b=2設定了HttpOnly屬性,不允許JavaScript通過指令碼來獲取到cookie資訊
由於當再次請求時,cookie會在請求頭中傳送到伺服器,由於cookie a=1設定了5秒後過期,在5秒後重新整理頁面,請求頭中的cookie只有a=1
在5秒內傳送二次請求,cookie a=1沒有失效,在請求頭中cookie a=1;b=2都會發送到伺服器
另外對於cookie的其他設定如expires、domain等在這裡也不多做介紹了
4. 重定向
當服務端返回301、302、307等狀態碼都代表資源已經被重定向到其他位置,301表示永久改變URI,302和307表示臨時重定向到某個URI
本例舉一個伺服器返回302狀態碼的例子,直接上程式碼:
// server.js const http = require('http'); const fs = require('fs') http.createServer((req, res) => { if (req.url === '/') { res.writeHead(302, { 'Location': '/redirect' }) res.end() } if (req.url === '/redirect') { res.end('redirect') } }).listen(8082);
訪問localhost:8082, 伺服器返回302狀態碼時,在相應頭中設定Location首部欄位,瀏覽器會繼續傳送請求到重定向的地址
HTTP與HTTPS的區別
首先說一下什麼是 HTTPS
HTTPS(全稱:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文字傳輸安全協議),是以安全為目標的HTTP通道,簡單講是HTTP的安全版。即HTTP下加入SSL層,HTTPS的安全基礎是SSL,因此加密的詳細內容就需要SSL。 --百度百科
HTTPS = HTTP+ 加密 + 認證 + 完整性保護
最主要是在應用層和傳輸層中間加了一個SSL(安全套階層),通常,HTTP 直接和 TCP 通訊。當使用 SSL 時,則演變成先和 SSL 通訊,再由 SSL 和 TCP 通訊。
HTTP與HTTPS的區別:
- (1) HTTP是明文傳輸,HTTPS是經過SSL加密後進行傳輸,只有客戶端和服務端根據公鑰和私鑰進行加密和解密能看到,中間任何傳輸環節無法獲取傳輸資訊,所以HTTPS比HTTP安全
- (2) HTTPS需要到數字證書認證機構進行購買
- (3) HTTP伺服器預設埠是80,HTTPS伺服器預設埠是443
本文主要介紹HTTP,關於HTTPS主要就介紹這麼多吧。
HTTP2
本想說點HTTP2的知識,奈何自己是小白,放個百度百科的連結吧 HTTP2 。
等後續隨著不斷的學習,再回來更新本文。
另外放一個HTTP1.1與HTTP2請求與相應對比的demo的連結 HTTP/2 is the future of the Web, and it is here!
最後,本文主要介紹了一些HTTP在web開發中的基礎知識,關於概念和圖解流程的截圖基本上都是來自 《TCP/IP詳解 卷1:協議》 、 《圖解HTTP》 、 《HTTP權威指南》 ,可放心參考。筆者功力實在有限,如有問題,請大家多多指出,相互學習和進步,也希望通過我的學習與實踐過程,整理出的筆記能對大家有所幫助,謝謝。