請(qǐng)求響應(yīng)原理及HTTP協(xié)議,請(qǐng)求報(bào)文與響應(yīng)報(bào)文

一、服務(wù)器端基礎(chǔ)概念

1.1 網(wǎng)站的組成

網(wǎng)站應(yīng)用程序主要分為兩大部分:客戶(hù)端和服務(wù)器端。
客戶(hù)端:在瀏覽器中運(yùn)行的部分,就是用戶(hù)看到并與之交互的界面程序。使用HTML、CSS、JavaScript構(gòu)建。
服務(wù)器端:在服務(wù)器中運(yùn)行的部分,負(fù)責(zé)存儲(chǔ)數(shù)據(jù)和處理應(yīng)用邏輯。

1.2 Node網(wǎng)站服務(wù)器

能夠提供網(wǎng)站訪(fǎng)問(wèn)服務(wù)的機(jī)器就是網(wǎng)站服務(wù)器,它能夠接收客戶(hù)端的請(qǐng)求,能夠?qū)φ?qǐng)求做出響應(yīng)。
一臺(tái)電腦需要安裝Node代碼運(yùn)行環(huán)境,使用Nodejs創(chuàng)建一個(gè)能接收對(duì)象和響應(yīng)請(qǐng)求的對(duì)象,就是Node網(wǎng)站服務(wù)器(可以沒(méi)有顯示器鼠標(biāo)鍵盤(pán),只有一個(gè)主機(jī))
電腦 ?? Node ?? 響應(yīng)請(qǐng)求的對(duì)象

1.3 IP地址

互聯(lián)網(wǎng)中設(shè)備的唯一標(biāo)識(shí)。
IP是Internet Protocol Address的簡(jiǎn)寫(xiě),代表互聯(lián)網(wǎng)協(xié)議地址

1.4 域名

由于IP地址難于記憶,所以我們一般不會(huì)使用IP地址直接訪(fǎng)問(wèn)網(wǎng)站。于是產(chǎn)生了域名的概念,所謂域名就是平時(shí)上網(wǎng)所使用的網(wǎng)址。
http://www.hellocode.com => http://136.168.215.101/
雖然在地址欄中輸入的是網(wǎng)址, 但是最終還是會(huì)將域名轉(zhuǎn)換為ip才能訪(fǎng)問(wèn)到指定的網(wǎng)站服務(wù)器。

1.5 端口

端口是計(jì)算機(jī)與外界通訊交流的出口,用來(lái)區(qū)分服務(wù)器電腦中提供的不同的服務(wù)。(比如web server和email server)

1.6 URL

統(tǒng)一資源定位符,又叫URL(Uniform Resource Locator),是專(zhuān)為標(biāo)識(shí)Internet網(wǎng)上資源位置而設(shè)的一種編址方式,我們平時(shí)所說(shuō)的網(wǎng)頁(yè)地址指的即是URL。
URL的組成
傳輸協(xié)議://服務(wù)器IP或域名:端口/資源所在位置標(biāo)識(shí)
https://localhost:8080/news/789.html

  • http:超文本傳輸協(xié)議。是一個(gè)基于請(qǐng)求與響應(yīng)、無(wú)狀態(tài)的、應(yīng)用層的協(xié)議,?;赥CP/IP協(xié)議傳輸數(shù)據(jù),互聯(lián)網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)協(xié)議,所有的WWW文件都必須遵守這個(gè)標(biāo)準(zhǔn)。設(shè)計(jì) HTTP 的初衷是為了提供一種發(fā)布和接收HTML頁(yè)面的方法。
  • https:是身披SSL外殼的 HTTP。一種通過(guò)計(jì)算機(jī)網(wǎng)絡(luò)進(jìn)行安全通信的傳輸協(xié)議,經(jīng)由HTTP進(jìn)行通信,利用SSL/TLS建立全信道,加密數(shù)據(jù)包。HTTPS使用的主要目的是提供對(duì)網(wǎng)站服務(wù)器的身份認(rèn)證,同時(shí)保護(hù)交換數(shù)據(jù)的隱私與完整性。

PS:TLS是傳輸層加密協(xié)議,前身是SSL協(xié)議,由網(wǎng)景公司1995年發(fā)布,有時(shí)候兩者不區(qū)分。

1.7 開(kāi)發(fā)過(guò)程中客戶(hù)端和服務(wù)器端說(shuō)明

在開(kāi)發(fā)階段,客戶(hù)端和服務(wù)器端使用同一臺(tái)電腦,即開(kāi)發(fā)人員電腦。
客戶(hù)端:瀏覽器
服務(wù)器端:Node.js
本地域名:localhost
本地IP:127.0.0.1

二、創(chuàng)建Web服務(wù)器

// 用于創(chuàng)建網(wǎng)站服務(wù)器的模塊
const http = require('http');
// 用于處理url地址
const url = require('url');
// app對(duì)象就是網(wǎng)站服務(wù)器對(duì)象
const app = http.createServer();
// 當(dāng)客戶(hù)端有請(qǐng)求來(lái)的時(shí)候
app.on('request', (req, res) => {//req是請(qǐng)求對(duì)象 res是響應(yīng)對(duì)象
  // 獲取請(qǐng)求方式
  // req.method
  console.log(req.method);
    
  // 獲取請(qǐng)求地址
  // req.url
  console.log(req.url);
    
  // 獲取請(qǐng)求報(bào)文信息
  // req.headers
  console.log(req.headers['accept']);
    
  res.writeHead(200, {
    'content-type': 'text/html;charset=utf8'
  });

  console.log(req.url);
  // 1) 要解析的url地址
  // 2) 將查詢(xún)參數(shù)解析成對(duì)象形式
  let { query, pathname } = url.parse(req.url, true);
  console.log(query.name)
  console.log(query.age)

  if (pathname == '/index' || pathname == '/') {
    res.end('<h2>歡迎來(lái)到首頁(yè)</h2>');
  }else if (pathname == '/list') {
    res.end('welcome to listpage');
  }else {
    res.end('not found');
  }
    
  if (req.method == 'POST') {
    res.end('post')
  } else if (req.method == 'GET') {
    res.end('get')
  }

  // res.end('<h2>hello user</h2>');
});
// 監(jiān)聽(tīng)端口
app.listen(3000);
console.log('網(wǎng)站服務(wù)器啟動(dòng)成功');

三、HTTP協(xié)議

超文本傳輸協(xié)議(英文:HyperText Transfer Protocol,縮寫(xiě):HTTP)規(guī)定了如何從網(wǎng)站服務(wù)器傳輸超文本到本地瀏覽器,它基于客戶(hù)端服務(wù)器架構(gòu)工作,是客戶(hù)端(用戶(hù))和服務(wù)器端(網(wǎng)站)請(qǐng)求和應(yīng)答的標(biāo)準(zhǔn)。

3.1 報(bào)文

在 HTTP 請(qǐng)求和響應(yīng)的過(guò)程中傳遞的數(shù)據(jù)塊就叫報(bào)文,包括要傳送的數(shù)據(jù)和一些附加信息,并且要遵守規(guī)定好的格式(鍵值對(duì)形式)。

請(qǐng)求報(bào)文:

請(qǐng)求行(request line)、請(qǐng)求頭(header)、空行請(qǐng)求體(body,可選的、包含數(shù)據(jù)) 組成。一次 HTTP 請(qǐng)求會(huì)按順序依次發(fā)送以上四個(gè)部分 (get 請(qǐng)求沒(méi)有請(qǐng)求體)。

我們用谷歌瀏覽器來(lái)對(duì)照著查看:

依次點(diǎn)擊找到請(qǐng)求報(bào)文
點(diǎn)擊 View source
請(qǐng)求報(bào)文的一般格式
  • 請(qǐng)求行:請(qǐng)求方式(<Request Method>) 資源路徑(<Request URL>) 協(xié)議/版本
    GET /users/9a996b97d637/publications?page=1&count=10 HTTP/1.1

    位置展示

    1. 常用請(qǐng)求方式 - Request Method:GET 請(qǐng)求(獲取)數(shù)據(jù)|POST 發(fā)送(添加)數(shù)據(jù)(更安全)|PUT 替換(修改)數(shù)據(jù)|DELETE 刪除數(shù)據(jù)
    2. 請(qǐng)求地址 - Request URL:端口號(hào)后面的地址。
  • 請(qǐng)求頭:附加的用來(lái)告知服務(wù)器的一些信息,以鍵值對(duì)的形式出現(xiàn)。

以下列舉部分常見(jiàn)屬性:

請(qǐng)求頭參數(shù) 描述
Host 目標(biāo)服務(wù)器的主機(jī)名 (ip 地址或域名)
User-Agent 發(fā)起請(qǐng)求的客戶(hù)端應(yīng)用程序相關(guān)信息,如操作系統(tǒng)、瀏覽器等信息
Referer 當(dāng)前文檔的完整 URL
Accept (Accept) 指定客戶(hù)端能接收哪些響應(yīng)信息類(lèi)型,如:image/jpg, text/html,專(zhuān)業(yè)術(shù)語(yǔ)稱(chēng)為MIME Type
Accept-Charset (Accept) 指定客戶(hù)端支持哪些字符集,如 gb2312
Accept-Encoding (Accept) 指定客戶(hù)端支持接收的內(nèi)容編碼(數(shù)據(jù)壓縮)方式,如 gzip, deflate
Accept-Language (Accept) 指定客戶(hù)端接受那些語(yǔ)言,如 zh-CN
Authorization (安全相關(guān)) 客戶(hù)端提供給服務(wù)端進(jìn)行權(quán)限認(rèn)證的信息
Cookie (安全相關(guān)) 客戶(hù)端攜帶的 cookie 信息(通常會(huì)存儲(chǔ)一個(gè)sessionID,通過(guò)這個(gè)令牌讓服務(wù)端鑒權(quán))
Content-Type (響應(yīng)頭通用) 內(nèi)容類(lèi)型,請(qǐng)求的與實(shí)體對(duì)應(yīng)的MIME信息。如果是 post 請(qǐng)求,會(huì)有這個(gè)參數(shù),默認(rèn)值為 application/x-www-form-urlencoded,表示請(qǐng)求體內(nèi)容使用 url 編碼
Content-Length (響應(yīng)頭通用) 請(qǐng)求體數(shù)據(jù)長(zhǎng)度
Connection (響應(yīng)頭通用) 指定客戶(hù)端和服務(wù)器之間請(qǐng)求/響應(yīng)連接的類(lèi)型,如:Keep-Alive 表示保持 TCP 持久連接
Transfer-Encodeing (響應(yīng)頭通用) 告知接收端為了報(bào)文的可靠傳輸,對(duì)報(bào)文使用什么樣的編碼格式
Cache-Control (響應(yīng)頭通用-緩存相關(guān)) 指定請(qǐng)求和響應(yīng)遵循的緩存機(jī)制
If-Modified-Since (緩存相關(guān)) 服務(wù)器會(huì)用該值與所請(qǐng)求資源的最后修改時(shí)間作對(duì)比,如相同返回304和空響應(yīng)體(節(jié)省帶寬),告訴客戶(hù)端資源未修改可直接讀取客戶(hù)端緩存;不同則返回200和最新的資源。值是上一次請(qǐng)求該資源時(shí)響應(yīng)頭的 Last-Modified
If-None-Match (緩存相關(guān)) 服務(wù)器會(huì)用該值與所請(qǐng)求資源的 ETag 作比較,匹配會(huì)返回304告訴客戶(hù)端直接使用本地緩存即可;不一致則返回200和新資源(還有新 ETag)。同時(shí)存在時(shí),優(yōu)先級(jí)高于 If-Modified-Since
  • 空白行:請(qǐng)求報(bào)文用來(lái)分隔 請(qǐng)求頭 和 請(qǐng)求體

Node 訪(fǎng)問(wèn)請(qǐng)求方法(method)、 請(qǐng)求地址(url) 和 請(qǐng)求頭(headers)

const http = require('http');
const app = http.createServer();

app.on('request',(req, res) => {
  // 獲取請(qǐng)求方式、請(qǐng)求地址 和請(qǐng)求頭
  const { method, url, headers } = req;
  // 獲取請(qǐng)求頭某個(gè)屬性信息
  const userAgent = headers['user-agent'];
})
  • 請(qǐng)求體(body):請(qǐng)求時(shí)攜帶的有效載荷數(shù)據(jù)。區(qū)別于 url 中的查詢(xún)參數(shù)??梢院?jiǎn)單地理解為,如果是 POST/PUT 請(qǐng)求,就有請(qǐng)求體。
響應(yīng)報(bào)文:

狀態(tài)行、響應(yīng)頭空行,響應(yīng)體(數(shù)據(jù)) 組成。服務(wù)器響應(yīng)時(shí),會(huì)按順序依次發(fā)送以上四個(gè)部分。

  • 狀態(tài)行:協(xié)議/版本號(hào) 狀態(tài)碼 狀態(tài)值(狀態(tài)描述)
    如:HTTP/1.1 200 OK
    1. 狀態(tài)碼 (Status Code):用以表示服務(wù)器 HTTP 響應(yīng)狀態(tài)的 3 位數(shù)字代碼:
狀態(tài)碼 描述
1xx 提示信息,服務(wù)器收到請(qǐng)求,需要請(qǐng)求者繼續(xù)執(zhí)行操作
2xx 成功,請(qǐng)求被成功接收并處理 200
3xx 重定向相關(guān) 304--所請(qǐng)求的資源未修改,客戶(hù)端會(huì)直接訪(fǎng)問(wèn)這個(gè)資源的緩存,不會(huì)返回任何資源
4xx 客戶(hù)端錯(cuò)誤 404--請(qǐng)求的資源沒(méi)有被找到 400-客戶(hù)端請(qǐng)求存在語(yǔ)法錯(cuò)誤
5xx 服務(wù)端錯(cuò)誤,500--服務(wù)器在處理請(qǐng)求的過(guò)程中發(fā)生了錯(cuò)誤 502/504--充當(dāng)網(wǎng)關(guān)或代理的服務(wù)器,從遠(yuǎn)端服務(wù)器接收到了一個(gè)無(wú)效的請(qǐng)求/未及時(shí)從遠(yuǎn)端服務(wù)器獲取請(qǐng)求
  • 響應(yīng)頭:類(lèi)似請(qǐng)求頭,告知客戶(hù)端的附加信息,是一系列 key-value 值。

以下列舉部分:
(部分通用屬性已經(jīng)在上面請(qǐng)求頭部分列出,不再重復(fù))

響應(yīng)頭參數(shù) 描述
Server 服務(wù)器軟件名,如 Tengine
Content-Encoding 對(duì)主體執(zhí)行的編碼方式
Content-Type 響應(yīng)主體的內(nèi)容類(lèi)型和字符集
Location 告知客戶(hù)端實(shí)體實(shí)際上位于何處;用于重定向
Set-Cookie (安全相關(guān)) 設(shè)置和頁(yè)面關(guān)聯(lián)的Cookie
Proxy-Authenticate (安全相關(guān)) 來(lái)自代理服務(wù)器的質(zhì)詢(xún)列表
Vary (協(xié)商相關(guān)) 一個(gè)列表,內(nèi)容來(lái)自于當(dāng)前請(qǐng)求頭的Key,比如Accept-Encoding、User-Agent等,用于發(fā)生相同請(qǐng)求的時(shí)候決定某個(gè)緩存的響應(yīng)版本是否可以用來(lái)發(fā)給客戶(hù)端
ETag (緩存相關(guān)) 實(shí)體標(biāo)記。服務(wù)端當(dāng)前資源實(shí)體特定版本的唯一標(biāo)識(shí)符,只要資源有變化就會(huì)重新生成。下一次請(qǐng)求該資源時(shí)會(huì)將這個(gè)值放入請(qǐng)求頭的 If-None-Match 來(lái)和服務(wù)端的 ETag 作比較。
Expires (緩存相關(guān)) 資源緩存過(guò)期時(shí)間。即客戶(hù)端在這個(gè)時(shí)間前獲取此資源都直接讀取本地緩存,而不再向服務(wù)器請(qǐng)求,作用是控制請(qǐng)求的頻率。Expires是http1.0的產(chǎn)物,已經(jīng)被http1.1的 Cache-Control: max-age 替代,仍然存在是為了兼容性
Last-Modified (緩存相關(guān)) 這個(gè)實(shí)體最后一次被修改的時(shí)間。與請(qǐng)求頭的 If-Modified-Since 配合使用

Vary

  1. Vary的內(nèi)容來(lái)自于當(dāng)前請(qǐng)求的請(qǐng)求頭的Key,如Accept-Encoding、User-Agent等;
  2. 緩存服務(wù)器進(jìn)行某接口的請(qǐng)求結(jié)果數(shù)據(jù)緩存時(shí),會(huì)將Vary一起緩存;
  3. 服務(wù)器緩存的Vary的內(nèi)容會(huì)作為當(dāng)前緩存數(shù)據(jù)是否可以作為請(qǐng)求結(jié)果返回給客戶(hù)端的判斷依據(jù);
  4. 先通過(guò)響應(yīng)數(shù)據(jù)中的Vary來(lái)判斷當(dāng)前緩存中同請(qǐng)求的數(shù)據(jù)的Vary是否失效,如果緩存中的Vary與服務(wù)器剛拿到的Vary不一致,則可以進(jìn)行更新。
  5. 當(dāng)Vary的值為“*”,意味著請(qǐng)求頭中的所有信息都不可作為是否從緩存服務(wù)器拿數(shù)據(jù)的判斷依據(jù)。

Content-Type
響應(yīng)的內(nèi)容類(lèi)型,告知客戶(hù)端你發(fā)送的數(shù)據(jù)內(nèi)容類(lèi)型,瀏覽器應(yīng)以什么形式、什么編碼讀取這個(gè)文件(否則可能解析錯(cuò)誤而出現(xiàn)亂碼)。
文件類(lèi)型有:text/plain(純文本)-對(duì)應(yīng).txt、text/html-對(duì)應(yīng).html、text/css-對(duì)應(yīng).css、application/javascript-對(duì)應(yīng).js、image/jpeg-對(duì)應(yīng).jpg或.jpeg、image/png-對(duì)應(yīng).png、application/json-對(duì)應(yīng).json

res.writeHead(200,{
  'content-type':'application/json; charset=utf-8' 
})
  • 空白行:同上,用來(lái)分隔 響應(yīng)頭 和 數(shù)據(jù)。

  • 響應(yīng)體:響應(yīng)的data

3.2 請(qǐng)求參數(shù)

客戶(hù)端向服務(wù)器端發(fā)送請(qǐng)求時(shí),有時(shí)需要攜帶一些客戶(hù)信息,客戶(hù)信息需要通過(guò)請(qǐng)求參數(shù)的形式傳遞到服務(wù)器端,比如登錄操作。

1. GET 請(qǐng)求參數(shù)

參數(shù)被放置在瀏覽器地址欄中,例如:http://localhost:3000/abc/xyz?name=zhangsan&age=20

  • 請(qǐng)求參數(shù)是?后面的部分,即name=zhangsan&age=20(通常我們稱(chēng)之為 查詢(xún)參數(shù))
// 系統(tǒng)模塊 url 用來(lái)處理 url 地址
const url = require('url')

app.on('request', (req, res) => {
  // `url.parse(req.url)` 將 url 路徑的各個(gè)部分解析出來(lái)并返回對(duì)象
  // 第二個(gè)參數(shù) true表示把查詢(xún)參數(shù) (url對(duì)象的query屬性值) 解析成對(duì)象格式
  let { query, pathname } = url.parse(req.url, true); // 解構(gòu)賦值獲取查詢(xún)參數(shù) query
  console.log(query.name) // zhangsan
  console.log(query.age) // 20
  console.log(pathname) // /abc/xyz
}
2. POST 請(qǐng)求參數(shù)
  • 參數(shù)被放置在請(qǐng)求體中進(jìn)行傳輸(Form Data)
  • 獲取POST參數(shù)需要使用 data 事件和 end 事件
  • 使用 querystring 系統(tǒng)模塊將參數(shù)轉(zhuǎn)換為對(duì)象格式
// 導(dǎo)入系統(tǒng)模塊querystring 用于將HTTP參數(shù)轉(zhuǎn)換為對(duì)象格式
const querystring = require('querystring');
app.on('request', (req, res) => {
  let postData = '';
  // 監(jiān)聽(tīng)參數(shù)傳輸事件
  req.on('data', (chunk) => postData += chunk;);
  // 監(jiān)聽(tīng)參數(shù)傳輸完畢事件
  req.on('end', () => { 
    console.log(postData);// 字符串
    console.log(querystring.parse(postData));  //把接收到參數(shù)字符串轉(zhuǎn)化為對(duì)象
  }); 
  res.end('ok');
});

3.2 路由

http://localhost:3000/index
http://localhost:3000/login
路由是指客戶(hù)端請(qǐng)求地址與服務(wù)器端程序代碼的對(duì)應(yīng)關(guān)系。簡(jiǎn)單的說(shuō),就是請(qǐng)求什么響應(yīng)什么。

// 當(dāng)客戶(hù)端發(fā)來(lái)請(qǐng)求的時(shí)候
app.on('request', (req, res) => {
  // 獲取請(qǐng)求方式
  const method = req.method.toLowerCase();
  // 獲取客戶(hù)端的請(qǐng)求路徑
  // const pathname = url.parse(req.url).pathname;
  let { pathname } = url.parse(req.url);
  if (pathname == '/' || pathname == '/index') {
    res.end('歡迎來(lái)到首頁(yè)');
  } else if (pathname == '/list') {
    res.end('歡迎來(lái)到列表頁(yè)頁(yè)');
  } else {
    res.end('抱歉, 您訪(fǎng)問(wèn)的頁(yè)面出游了');
  }
});

3.3 靜態(tài)資源

服務(wù)器端不需要處理,可以直接響應(yīng)給客戶(hù)端的資源就是靜態(tài)資源,例如 CSS、JavaScript、image文件。
舉例:http://localhost:8080/images/logo.png

3.4 動(dòng)態(tài)資源

相同的請(qǐng)求地址不同的響應(yīng)資源,這種資源就是動(dòng)態(tài)資源。
http://localhost:8080/article?id=123
http://localhost:8080/article?id=456

3.5 客戶(hù)端請(qǐng)求途徑

  1. GET 方式
  • 瀏覽器地址欄訪(fǎng)問(wèn)
  • link 標(biāo)簽的 href 屬性
  • script 標(biāo)簽的 src 屬性
  • img 標(biāo)簽的 src 屬性
  • Form 表單提交 (表單的默認(rèn)跳轉(zhuǎn)行為)
  1. POST 方式
  • Form 表單提交 (在form表單標(biāo)簽里寫(xiě)上method='post')
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容