1. 瞭解部分有意義的頭部信息
頭部信息 | |
Catch-Control:max-age=100 | 靜態資源緩存100s |
Content-Type,Content-Encoding 等 | 用來約束數據類型 |
Cookie | 保持會話信息 |
CORS | 實現跨域並保持安全性限制 |
2. 頁面如何到達瀏覽器並展現給用戶
Redirect(跳轉)--> App catch(應用緩存)-->DNS(DNS查找)-->TCP (創建TCP鏈接)-->Request(發送請求)
-->Response(接受響應)
1. redirect : 對於瀏覽器已經記錄過的301請求,直接跳轉,屬於純客戶端的行爲。
2. cache: 根據資源是否有設置cache control ,然後判斷緩存。如果超時則要重新請求緩存。
3. DNS查詢:當通過瀏覽器請求一個web頁面,瀏覽器會創建一個線程去處理這個請求,隨後開始遠程dns查找,遠程dns服務器會將輸入的URL對應的IP地址返回給瀏覽器。(DNS域名系統)
4. TCP連接: 三次握手 (HTTP請求是在TCP連接上發送的,一個TCP連接可以發送多個HTTP請求)
5. 發送請求: 數據經過代理服務器或代理服務器緩存直接讀取。瀏覽器通過連接發送一個HTTP GET請求到Web服務端。
6. 接受響應。Web服務端找到請求的資源,然後在HTTP響應中將其返回,狀態200表示響應正常。
常用狀態碼:
- 200: 服務端成功響應
- 301: 永久重定向
- 302: 臨時重定向
- 403: 請求被拒絕
- 404: 服務端找不到請求的資源
- 500: 處理請求時出錯
- 503: 服務不可用
- 504: 網關超時
最後,瀏覽器收到頁面HTML,就開始解析並渲染頁面內容
3. 網絡協議分層
經典的五層網路模型:
物理層:定義物理設備如何傳輸數據(電腦硬件,網卡端口,光纜...)。
數據鏈路層: 在通信的實體間建立數據鏈路連接。
網絡層:爲數據在節點之間傳輸創建邏輯鏈路。
傳輸層: 向用戶提供可靠的端對端(end-to-end)服務。更多情況下實用TCP/IP協議傳輸數據。
定義如何傳輸數據,傳輸數據的方式。
應用層:爲應用軟件提供了很多的服務。構建於TCP協議之上。
4. HTTP/1.1
pipeline: 可以通過聲明,在同一個連接裏發送多個請求
增加host:可以在同一臺物理服務器跑多個不同的web服務
HTTP 2 : 所有數據以二進制傳輸,同一個連接裏發送多個請求不再需要按照順序,頭信息壓縮以及推送等提高效率的功能。
推送功能(請求html的同時,可以將html中需要引用到的JavaScript文件和css文件推送到客戶端,實現html和css js文件的發送順序是閉型的,提高整體的傳輸效率)
5. 三次握手
爲了防止服務端開啓無用的連接。(網絡服務器延遲等)
瀏覽器通過與遠程web服務端的三次握手來建立一個TCP/IP請求。這個握手由瀏覽器與遠程服務端之間的SYN(標誌位),SYN-ACK,ACK消息組成。(SYN:同步序列編號)
圖1 TCP 三次握手
6. 統一資源定位器 Uniform Resource Locators
scheme://host.domain:port/path/filename
說明:
- scheme - 定義因特網服務的類型。最常見的類型是 http
- host - 定義域主機(http 的默認主機是 www)
- domain - 定義因特網域名,比如 runoob.com
- :port - 定義主機上的端口號(http 的默認端口號是 80)
- path - 定義服務器上的路徑(如果省略,則文檔必須位於網站的根目錄中)。
- filename - 定義文檔/資源的名稱
7. 創建一個最簡單的web服務
server.js文件下創建如下內容:
const http = require('http')
http.createServer((request, response) => {
console.log('request', request.url)
response.end('123')
}).listen(8888)
console.log('server listening on 8888')
啓動: node server.js
瀏覽器打開:localhost:8888, 查看內容
利用curl命令查看http報文信息
$ curl -v www.baidu.com
能得到報文頭和header的內容:
* Rebuilt URL to: www.baidu.com/
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 14.215.177.39...
* TCP_NODELAY set
* Connected to www.baidu.com (14.215.177.39) port 80 (#0)
> GET / HTTP/1.1
> Host: www.baidu.com
> User-Agent: curl/7.57.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2381
< Content-Type: text/html
< Date: Fri, 17 Aug 2018 05:07:47 GMT
< Etag: "588604cf-94d"
< Last-Modified: Mon, 23 Jan 2017 13:27:43 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<
{ [1048 bytes data]
100 2381 100 2381 0 0 2381 0 0:00:01 --:--:-- 0:00:01 74406
8. 跨域請求
新建server.js: 創建請求
//在8888端口中請求了一個8887端口的內容
const http = require('http')
const fs = require('fs')
http.createServer((request, response) => { //創建請求
console.log('request', request.url)
const html = fs.readFileSync('test.html', 'utf8'); //同步讀取html內容
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html) //響應內容
}).listen(8888)
console.log('server listening on 8888')
server2.js
const http = require('http')
http.createServer((request, response) => {
console.log('request', request.url)
// 允許跨域請求 寫返回的狀態
response.writeHead(200, {
'Access-Control-Allow-Origin': '*'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
test.html 中向8887端口發送請求:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script>
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://127.0.0.1:8887/')
xhr.send()
</script>
</body>
</html>
發送預請求:(跨域限制:保證服務端的安全)
預請求 | |
---|---|
允許的方法 | GRT、POST、HEAD |
允許Content-Type |
text/plain、multipart/formdata、 application/x-www-form-urlencoded |
通過options請求獲得服務器的認可
server.js:
//相當於在8888端口中請求了一個8887端口的內容
const http = require('http')
const fs = require('fs')
http.createServer((request, response) => { //創建請求
console.log('request', request.url)
const html = fs.readFileSync('test.html', 'utf8'); //同步讀取html內容
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html) //響應內容
}).listen(8888)
console.log('server listening on 8888')
server2.js:
const http = require('http')
http.createServer((request, response) => {
console.log('request', request.url)
// 允許跨域請求 寫返回的狀態
response.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'X-Test-Cors', //允許自定義的頭
'Access-Control-Allow-Methods': 'PUT, POST, GET',
'Access-Control-Max-Age': '1000'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
Access-Control-Max-Age 這個響應首部表示 preflight request (預檢請求)的返回結果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被緩存多久。
緩存Cache-Control: (哈希值用於刷新瀏覽器緩存)
可緩存性 | |
---|---|
public | 任何地方都可以對返回的內容緩存 |
private | 只有發起請求的地方可以 |
no-cache | 通過服務器端驗證後才能使用本地緩存 |
到期 | |
max-age=<seconds> |
緩存還有多過期。 可以將max-age設置爲0, 從而讓每次訪問時緩存都進行刷新。 |
max-stale=<seconds> | 可以使用多久的過期緩存 |
重新驗證 | |
must-revalidata | 如果要提供過期的數據給客戶端,則必須向服務器驗證數據的新鮮度 |
緩存驗證:
緩存驗證 | |
---|---|
Last-Modified | '123' 標記此文件在服務器端最後被修改的時間 |
Etag | '777' 用於標示URL對象是否改變 |
no-store: 忽略緩存
9. Cookie和Session
Cookie:1. 通過Set-Cookie設置 2. 下次請求會自動帶上 3.鍵值對,可以設置多個
屬性:
Cookie屬性 | |
---|---|
max-age和expires | 設置過期時間 |
Secure | 只在https的時候發送 |
HttpOnly | 無法通過document.cookie訪問,保證用戶安全 |
服務端server.js:
const http = require('http')
const fs = require('fs')
http.createServer((request, response) => { //創建請求
console.log('request', request.url)
if (request.url === '/') {
const html = fs.readFileSync('test.html', 'utf8'); //同步讀取html內容
response.writeHead(200, {
'Content-Type': 'text/html',
'Set-Cookie': ['abc=123; max-age=2','id=456'], //可以以數組方式傳遞多個cookie 前面一個字段的有效時間爲兩秒
// 2s後abc=123這個數據就過期,request headers中就不會有這個信息出現
})
response.end(html) //響應內容
}
}).listen(8888)
console.log('server listening on 8888')
客戶端test.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<div>content</div>
<script src="/script.js">
console.log(document.cookie)
</script>
</body>
</html>
10. 數據協商:
客戶端請求時會聲明希望拿到的數據格式和限制,服務端根據請求頭返回不同的數據
客戶端請求 | |
---|---|
Accept |
聲明想要的數據類型 [主類型]/[子類型] ,如text/html,image/jpg |
Accept-Encoding |
聲明進行傳輸的編碼方式 主要是數據壓縮的算法,如gzip, deflate, br |
Accept-Language |
聲明希望返回信息的語言 如zh-CN,zh;q=0.9(q表權重,0~1) |
Usrer-Agent |
聲明瀏覽器和操作系統的相關信息 |
服務端返回 | |
---|---|
Content-Type |
聲明返回的數據格式, 如'X-Content-Type-Options':'nosniff',可阻止瀏覽器自行猜測返回數據類型而引發的安全問題。 |
Content-Encoding |
聲明返回的編碼方式,即數據壓縮 |
Content-Language |
聲明返回的語言 |
表單發送到服務器時的三種編碼方式:
enctype(編碼方式) | |
---|---|
application/x-www-form-urlencoded | 默認的編碼方式。但是在用文本的傳輸和MP3等大型文件的時候,使用這種編碼就顯得 效率低下。 |
multipart/form-data | 指定傳輸數據爲二進制類型,比如圖片、mp3、文件 |
text/plain | 純文體的傳輸。空格轉換爲 “+” 加號,但不對特殊字符編碼 |
將表單的enctype(編碼方式)定義爲"multipart/form-data":適用於上傳文件的方式
<form method="POST" enctype="multipart/form-data">
<input type="text" name="name">
<input type="password" name="psd">
<input type="submit">
</form>
提交表單之後:能夠在瀏覽器請求頭中得到如下信息:
Request Header:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryCoZcoYObU8Ubn5U2
boundary後的字符串用於分割表單中的每一個部分。
------WebKitFormBoundaryCoZcoYObU8Ubn5U2
Content-Disposition: form-data; name="name"
8月5
------WebKitFormBoundaryCoZcoYObU8Ubn5U2
Content-Disposition: form-data; name="psd"
sss
------WebKitFormBoundaryCoZcoYObU8Ubn5U2--
控制檯中的connection ID 代表TCP連接的ID,可以用來區分是否用的同一個連接。
connection: close, //沒有重複利用TCP連接,每次TCP請求發送完就關閉了
(HTTP請求是在TCP連接上發送的,一個TCP連接可以發送多個HTTP請求)
11. Content Security Policy (內容安全策略)
1. 限制資源獲取 2. 報告資源獲取越權
CSP 大大增強了網頁的安全性。
兩種方法啓動CSP:
1. 一種是通過 HTTP 頭信息的Content-Security-Policy
的字段
Content-Security-Policy: script-src 'self'; object-src 'none';
2. 通過網頁的<meta>
標籤。
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'">
( 腳本:只信任當前域名 <object>
標籤:不信任任何URL,即不加載任何資源)
只允許通過http,https方式加載src資源:
'Content-Security-Policy': 'default-src http: https:'
只能根據本域名下的JavaScript內容進行加載:
'Content-Security-Policy': 'default-src self'