一、服務(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ì)照著查看:



-
請(qǐng)求行:請(qǐng)求方式(
<Request Method>) 資源路徑(<Request URL>) 協(xié)議/版本
如GET /users/9a996b97d637/publications?page=1&count=10 HTTP/1.1
位置展示- 常用請(qǐng)求方式 - Request Method:GET 請(qǐng)求(獲取)數(shù)據(jù)|POST 發(fā)送(添加)數(shù)據(jù)(更安全)|PUT 替換(修改)數(shù)據(jù)|DELETE 刪除數(shù)據(jù)
- 請(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- 狀態(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:
- Vary的內(nèi)容來(lái)自于當(dāng)前請(qǐng)求的請(qǐng)求頭的Key,如
Accept-Encoding、User-Agent等; - 緩存服務(wù)器進(jìn)行某接口的請(qǐng)求結(jié)果數(shù)據(jù)緩存時(shí),會(huì)將Vary一起緩存;
- 服務(wù)器緩存的Vary的內(nèi)容會(huì)作為當(dāng)前緩存數(shù)據(jù)是否可以作為請(qǐng)求結(jié)果返回給客戶(hù)端的判斷依據(jù);
- 先通過(guò)響應(yīng)數(shù)據(jù)中的Vary來(lái)判斷當(dāng)前緩存中同請(qǐng)求的數(shù)據(jù)的Vary是否失效,如果緩存中的Vary與服務(wù)器剛拿到的Vary不一致,則可以進(jìn)行更新。
- 當(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)求途徑
- GET 方式
- 瀏覽器地址欄訪(fǎng)問(wèn)
- link 標(biāo)簽的 href 屬性
- script 標(biāo)簽的 src 屬性
- img 標(biāo)簽的 src 屬性
- Form 表單提交 (表單的默認(rèn)跳轉(zhuǎn)行為)
- POST 方式
- Form 表單提交 (在form表單標(biāo)簽里寫(xiě)上
method='post')


