HTTP 協議
概念
- HTTP(hypertext transport protocol)協議;中文叫超文本傳輸協議,是一種基于TCP/IP的應用層通信協議
- 這個協議詳細規定了 瀏覽器 和萬維網 服務器 之間互相通信的規則。
- 協議中主要規定了兩個方面的內容
- 客戶端:用來向服務器發送數據,可以被稱之為請求報文
- 服務端:向客戶端返回數據,可以被稱之為響應報文
客戶端請求規范(請求報文組成)
1. 請求行
- 由三部分組成:
- 請求方法
- 常用的請求方法
- GET:獲取資源
- POST:提交資源
- PUT:更新資源
- DELETE:刪除資源
- HEAD:獲取資源的元信息
- OPTIONS:獲取服務器支持的請求方法
- 請求URL??:統一資源定位符
http://www.example.com:8080/path/to/resource?query=param#fragment
// 協議:http(https、ftp、ssh等) 通信協議
// 域名:www.example.com 服務器地址
// 端口:8080 端口號,默認端口號為80,可省略
// 路徑:/path/to/resource 資源位置
// 查詢參數:?query=param 用于向服務器傳遞額外信息
// 錨點:#fragment 頁面內定位(瀏覽器端使用),哈希(錨點鏈接)
- HTTP 協議版本
- 例如:HTTP/1.1或HTTP/2
2.請求頭
- 采用鍵值對格式:頭名: 頭值
頭名 | 作用 | 示例 |
---|---|---|
Host | 目標主機 | Host: www.example.com |
User-Agent | 客戶端標識,用戶代理,客戶端字符串標識,服務器可以通過這個標識來識別這個請求來自User-Agent,哪個客戶端 ,一般在PC端和手機端的區分 | User-Agent: Mozilla/5.0 |
Accept | 可接收的響應類型 | Accept: text/html |
Accept-Encoding | 可接受的壓縮編碼類型 | Accept-Encoding: gzip, deflate |
Cookie | 客戶端存儲信息 | Cookie: session_id=abc123 |
Content-Type | 請求體類型 | Content-Type: application/json |
Authorization | 身份驗證 | Authorization: Bearer token |
Connection | 連接的設置 keep-alive(保持連接);close(關閉連接) | Connection: keep-alive |
Cache-Control | 緩存控制 max-age = 0 (沒有緩存) | Cache-Control: no-cache |
Upgrade-Insecure-Requests | 將網頁中的http請求轉化為https請求(很少用)老網站升級 | Upgrade-Insecure-Requests: 1 |
Accept-Language | 可接受的語言 | Accept-Language: en-US,en;q=0.9 |
3.空行
- 用于分隔請求頭和請求體
4.請求體
包含客戶端發送給服務器的數據:
?- ?GET請求??:通常沒有請求體
- ??POST/PUT請求??:包含表單數據、JSON、文件等
- 用于向服務器提交數據,例如表單數據、JSON數據等。
- 請求體的格式取決于請求頭中的Content-Type字段。
- 常見的請求體格式有:
- application/x-www-form-urlencoded:表單數據
- multipart/form-data:文件上傳
- 表單:name=John&age=30
- JSON:{“name”: “John”, “age”: 30}
- XML:John
服務端響應規范(響應報文組成)
1.狀態行(Status Line)
- 由三部分組成:
- HTTP協議版本
- HTTP/1.1:HTTP協議版本號
- 狀態碼(Status Code)
//1xx 信息響應 ,請求已接收,繼續處理 //2xx 成功響應,請求成功處理 //3xx 重定向,需要進一步操作 //4xx 客戶端錯誤,客戶端請求錯誤 //5xx 服務器錯誤,服務器處理錯誤 //例如: //200 OK:請求成功 //301 Moved Permanently:永久重定向 //404 Not Found:資源不存在 //500 Internal Server Error:服務器內部錯誤
- 狀態描述(Reason Phrase)
- OK:請求成功
- Not Found:請求的資源不存在
- Internal Server Error:服務器內部錯誤
- Bad Request:客戶端請求錯誤
- Unauthorized:未授權
- Forbidden:禁止訪問
- HTTP協議版本
- 響應頭
服務器返回的元數據信息
常見的響應頭 | 作用 | 示例 |
---|---|---|
Content-Type | 響應體類型 | Content-Type: text/html;charset=utf-8,設置響應體的數據類型以及字符集,響應體為html,字符集utf-8 |
Content-Length | 響應體的長度,單位為字節 | Content-Length: 1234 |
Set-Cookie | 設置客戶端Cookie | Set-Cookie: session_id=abc123 |
Cache-Control | 緩存控制 | Cache-Control: max-age=3600 |
Location | 重定向目標 | Location: /new-path |
- 空行
分隔響應頭和響應體的空行 - 響應體
服務器返回的實際內容 - HTML頁面
- JSON/XML數據
- 圖片/視頻等二進制文件
- JavaScript/CSS等資源
MIME類型處理
HTTP通過Content-Type頭指定數據的MIME類型
文件類型 | MIME類型 | 說明 |
---|---|---|
HTML | text/html | 網頁內容 |
CSS | text/css | 樣式表 |
JavaScript | application/javascript | 腳本 |
JSON | application/json | 結構化數據 |
TEXT | text/plain | 文本 |
JPEG | image/jpeg | 圖片 |
application/pdf | 文檔 | |
XML | application/xml | XML數據 |
ZIP | application/zip | 壓縮文件 |
GIF | image/gif | GIF圖片 |
PNG | image/png | PNG圖片 |
SVG | image/svg+xml | SVG圖片 |
MP3 | audio/mpeg | 音頻 |
MP4 | video/mp4 | 視頻 |
連接管理
HTTP協議支持不同的連接策略
- ?HTTP/1.0??:短連接(每次請求新建連接)
- ??HTTP/1.1??:持久連接(默認啟用Keep-Alive)
- ??HTTP/2??:多路復用(多個請求共享一個連接)
- ??HTTP/3??:QUIC協議(基于UDP的快速傳輸協議)
Node.js 創建 HTTP 服務
Node.js 內置的 http 模塊可以輕松創建高性能的 HTTP 服務器
// 1.導入 http 模塊
const http = require('http');//2. 創建服務對象 create 創建 server 服務
//request 意為請求. 是對請求報文的封裝對象, 通過 request 對象可以獲得請求報文的數據
//response 意為響應. 是對響應報文的封裝對象, 通過 response 對象可以設置響應報文
const server = http.createServer((req, res) => {// 設置響應頭//res.setHeader('Content-Type', 'text/plain');// 設置響應頭防止中文亂碼res.setHeader('content-type','text/html;charset=utf-8');// 發送響應res.end('哈哈哈Hello, World!\n');})
//3. 啟動服務器監聽指定端口
server.listen(3000, () => {console.log('服務啟動成功, 端口號為 3000')
})
注意:
- 1.響應內容中文亂碼的解決辦法
// 設置響應頭防止中文亂碼
res.setHeader('content-type','text/html;charset=utf-8');
-
2.端口號被占用
解決辦法:修改端口號
-
3.HTTP 協議默認端口是 80 。HTTPS 協議的默認端口是 443, HTTP 服務開發常用端口有 3000,
8080,8090,9000 等
獲取 HTTP 請求報文(請求對象 (request) 詳解)
想要獲取請求的數據,需要通過 request 對象
const server = http.createServer((req, res) => {// 1. 獲取請求方法const method = req.method; // GET, POST 等// 2. 解析請求URLconst url = new URL(req.url, `http://${req.headers.host}`);
// URL {
// href: 'http://localhost:3000/?a=1',
// origin: 'http://localhost:3000',
// protocol: 'http:',
// username: '',
// password: '',
// host: 'localhost:3000',
// hostname: 'localhost',
// port: '3000',
// pathname: '/',
// search: '?a=1',
// searchParams: URLSearchParams { 'a' => '1' },
// hash: ''
// } url1111// 3. 獲取請求路徑const pathname = url.pathname; // 如:'/api/user'// 4. 獲取查詢參數const query = Object.fromEntries(url.searchParams);// 5. 獲取請求頭const contentType = req.headers['content-type'];// 6. 獲取請求體 (POST/PUT)let body = '';req.on('data', chunk => body += chunk);req.on('end', () => {console.log('請求體內容:', body);// 設置響應res.end(`請求方法: ${method}, 路徑: ${pathname}`);});
});
注意:
- request.headers 將請求信息轉化成一個對象,并將屬性名都轉化成了『小寫』
- 關于路徑:如果訪問網站的時候,只填寫了 IP 地址或者是域名信息,此時請求的路徑為『 / 』
- 關于 favicon.ico:這個請求是屬于瀏覽器自動發送的請求
const http = require('http');
const server = http.createServer((req, res) => {// 獲取請求方法和路徑// const {method, url} = req;// console.log(req.url,'xxxxxxx') // /sm/api/user http://localhost:3000/sm/api/user post// console.log(req.url,'xxxxxxx') // / http://localhost:3000/ Get// console.log(req.url,'xxxxxxx') // /about http://localhost:3000/about Getconst { method } = req;const url = new URL(req.url, `http://${req.headers.host}`);const pathname = url.pathname;// 設置響應頭res.setHeader('Content-Type', 'text/html; charset=utf-8');// 路由處理if (method === 'GET' && pathname === '/') {res.end('<h1>首頁</h1>');} else if (method === 'GET' && pathname === '/about') {res.end('<h1>關于我們</h1>');} else if (method === 'POST' && pathname === '/api/user') {// 處理API請求...res.end(JSON.stringify({ code: 200, data: { name: '蘇木' } }));} else {res.statusCode = 404;res.end('<h1>頁面不存在</h1>');}});server.listen(3000, () => {console.log('服務器已啟動,監聽端口 3000');});
設置 HTTP 響應報文(響應對象 (response) 詳解)
const server = http.createServer((req, res) => {// 1. 設置狀態碼res.statusCode = 200; // 默認200,可以設置為404等// 2. 設置響應頭res.setHeader('Content-Type', 'application/json');res.setHeader('Cache-Control', 'public, max-age=3600');// 3. 寫入響應內容(多次寫入)res.write('第一部分數據');res.write('第二部分數據');// 4. 結束響應并發送res.end('最后內容');// 5. 一次性寫入并結束// res.end(JSON.stringify({ message: 'Hello' }));
});
靜態文件服務
實現靜態資源服務器
const http = require('http');
const fs = require('fs');
const path = require('path');// MIME 類型映射
const mimeTypes = {'.html': 'text/html','.js': 'text/javascript','.css': 'text/css','.json': 'application/json','.png': 'image/png','.jpg': 'image/jpeg','.gif': 'image/gif','.svg': 'image/svg+xml',
};const server = http.createServer((req, res) => {// 獲取文件路徑 public 文件夾下的文件的靜態資源const filePath = path.join(__dirname, 'public', req.url);// 獲取文件擴展名const extname = path.extname(filePath);// 設置默認內容類型let contentType = mimeTypes[extname] || 'application/octet-stream';// 讀取文件fs.readFile(filePath, (err, data) => {if (err) {// 文件不存在if (err.code === 'ENOENT') {res.statusCode = 404;res.end('<h1>404 Not Found</h1>');} else {// 成功讀取文件res.setHeader('Content-Type', contentType);res.end(data);}});
});server.listen(9000);// 訪問: http://localhost:3000/232.png
// 結果如下