數據是以報文爲載體在瀏覽器與服務器之間傳輸的,報文(message)是網絡中交換與傳輸的數據單元,即站點一次性要發送的數據塊,它由三個部分組成:
- 起始行:報文的第一行就是起始行。請求報文的起始行描述服務器應該執行的操作和HTTP的版本,響應報文的起始行承載了狀態信息和操作產生的結果數據;
- 首部:起始行後面有零個或多個首部字段。每個首部字段都包含一個名值對,首部以一個空行結束;
- 主體:空行之後就是可選的報文主體,其中包含了所有類型的數據。請求主體中包括了要發送給Web服務器的數據;響應主體中裝載了要返回給客戶端的數據。起始行和首部都是文本形式且都是結構化的,而主體則不同,主體中可以包含任意的二進制數據(比如圖片、視頻、音頻、軟件程序)。當然,主體中也可以包含文本。
下面分別用GET請求和POST請求實現一個登錄功能,先新建http文件夾,內部新建index.js
、login.html
兩個文件。
一、GET請求
只能傳輸少量數據(<32K,通過報文頭傳輸)。
index.js
:
let http = require('http')
http.createServer((req, res) => {
// 我們先打印一下req.url,看看是什麼
console.log(req.url)
}).listen(8080)
login.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登錄</title>
</head>
<body>
<!--action:主機+端口+被訪頁面的名稱,method:get-->
<form action="http://localhost:8080/login" method="get">
賬戶:<input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
<!--type爲submit,點擊後就會觸發上面的action-->
<input type="submit" value="登錄">
</form>
</body>
</html>
雙擊login.html訪問該頁面,賬戶輸入zhangsan
,密碼輸入123
,點擊登錄,命令行打印:/login?username=zhangsan&password=123
,說明GET請求下,表單數據通過報文頭傳輸。
url模塊
如何獲取問號後面字符串(即query)中的各項數據呢?如果通過字符串的split方法進行分割取值的話感覺很麻煩,有沒有更好的辦法?其實Node提供了一個url模塊,index.js
引入url:
let http = require('http')
let url = require('url')
http.createServer((req, res) => {
// 調用url的parse方法,傳入req.url
console.log(url.parse(req.url))
}).listen(8080)
執行node index.js
,login.html
頁面輸入內容點擊登錄後,命令行打印:
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: '?username=zhangsan&password=123',
query: 'username=zhangsan&password=123', // 注意query這一行
pathname: '/login',
path: '/login?username=zhangsan&password=123',
href: '/login?username=zhangsan&password=123' }
index.js
代碼做一個小改動:console.log(url.parse(req.url, true))
,即在url.parse方法中添加一個參數true
(表示將query字符串處理成對象形式),接着重複上面的步驟再執行一次,看命令行結果:
Url {
...
query:
[Object: null prototype] { username: 'zhangsan', password: '123' }, // 注意,這一行變了
...
}
現在,可以獲取到我們需要的pathname
和query
了(之後再去數據庫裏匹配數據,該部分類容省略)
let {pathname, query} = url.parse(req.url, true)
console.log(pathname, query)
執行之後打印:
/login [Object: null prototype] { username: 'zhangsan', password: '123' }
二、POST請求
既能接收數據,又能提交數據(<2G,通過報文體傳輸)。採用POST請求傳輸的數據可能很大,所以Node是讓這些數據分段傳輸的,服務器接收到多個數據片段後再拼接起來使用。
index.js
:
let http = require('http')
// 將post的數據從字符串類型轉化爲我們需要的json類型
let querystring = require('querystring')
http.createServer((req, res) => {
// Node提供的on方法表示事件監聽,'data'表示傳輸的每個數據片段,每傳輸一段就會執行一遍on('data')方法;
// post請求發送的數據不僅侷限於字符串,還有圖片或文件,所以回調函數以buffer這種二進制格式作爲參數;
// 因爲例子中傳輸的數據長度很小,只需一個片段,但是實際生產中片段很多,所以用數組result去承載所有的數據片段
let result = []
req.on('data', buffer => {
console.log(buffer) // No.1
console.log(buffer.toString()) // No.2
result.push(buffer)
})
// on('end')方法監聽傳輸完成,利用Bufer提供的concat方法把所有零碎片段拼接起來
req.on('end', () => {
console.log(result) // No.3
let data = Buffer.concat(result)
console.log(data) // No.4
console.log(data.toString()) // No.5
console.log(querystring.parse(data.toString())) // No.6
})
}).listen(8080)
login.html
和上面的基本相同,只是將form標籤的method的值改爲post
。
啓動Node服務,登錄頁面輸入zhangsan
和123
點擊登錄,命令行打印:
注:上面兩處爲buffer數據執行toString方法其實是不嚴謹的,對於簡單數據(字符串)還可以,但是對圖片和文件數據就沒意義了,所以僅作爲舉例說明。