Node的 http 模組只對HTTP報文的頭部進行了解析,然後觸發 request
事件。如果請求中還帶有內容部分(如 POST 請求,它具有報頭和內容),內容部分需要使用者自行接收和解析。
通過報頭的 Transfer-Encoding
或 Content-Length
即可判斷請求中是否帶有內容
欄位名稱 | 含義 |
---|---|
Transfer-Encoding | 指定報文主體的傳輸編碼方式 |
Content-Length | 報文主體的大小 |
寫個方法判斷是否有報文主體
const hasBody = function(req) {
return 'transfer-encoding' in req.headers || 'content-length' in req.headers;
};
複製程式碼
接收資料
報文內容部分會通過 data
事件觸發,我們只需以流的方式處理即可,不要在訂閱 data
事件的時候使用 +=
的形式拼裝資料,這樣會亂碼的。
function handle(req, res) {
if (hasBody(req)) {
var buffers = [];
req.on('data', function (chunk) {
buffers.push(chunk);
});
req.on('end', function () {
const POST = Buffer.concat(buffers).toString();
});
}
}
複製程式碼
1. POST傳送的是表單的資料
如果在頁面中使用表單提交一個post請求,我們的程式碼大概是這樣的。
<form action="/upload" method="post">
<label for="username">使用者名稱:</label>
<input type="text" name="username" id="username" />
<label for="password">密碼:</label>
<input type="password" name="password" id="password" />
<input type="submit" />
</form>
複製程式碼
預設的表單提交,請求頭中的 Content-Type
欄位值為 application/x-www-form-urlencoded
Content-Type: application/x-www-form-urlencoded
複製程式碼
寫一個判斷內容型別的方法
const mime = function (req) {
const str = req.headers['content-type'] || '';
return str.split(';')[0];
};
複製程式碼
它的報文體內容跟查詢字串相同
username=Tom&password=123456
複製程式碼
解析表單資料使用querystring
模組中的parse
方法
const querystring = require('querystring')
function handleForm (req, res) {
const isFrom = mime(req) === 'application/x-www-form-urlencoded'
if (hasBody(req) && isFrom) {
var buffers = [];
req.on('data', function (chunk) {
buffers.push(chunk);
});
req.on('end', function () {
let requestBody = Buffer.concat(buffers).toString();
requestBody = querystring.parse(requestBody)
});
}
}
複製程式碼
2. POST傳送的是JSON的資料
如果在頁面中使用axios傳送post請求,我們的程式碼大概是這樣的。
axios.post('/user', {
username: 'Tom',
password: '123456'
})
複製程式碼
預設的JSON提交,請求頭中的 Content-Type
欄位值為 application/json
,在 Content-Type
中可能還附帶編碼資訊 charset=utf-8
Content-Type: application/json; charset=utf-8
複製程式碼
它的報文體內容跟JSON格式的字串相同
{
"name": "Tom",
"password": "123456"
}
複製程式碼
解析JSON資料使用 JSON.parse
方法。
function handleJson (req, res) {
const isJson = mime(req) === 'application/json'
if (hasBody(req) && isJson) {
var buffers = [];
req.on('data', function (chunk) {
buffers.push(chunk);
});
req.on('end', function () {
let requestBody = Buffer.concat(buffers).toString();
try {
requestBody = JSON.parse(requestBody)
} catch (error) {
console.log(error)
}
});
}
}
複製程式碼
3. POST傳送的是檔案資料
如果在頁面中使用表單提交檔案請求,我們的程式碼大概是這樣的。
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="avatar" id="avatar">
<input type="submit" />
</form>
複製程式碼
預設的上傳檔案提交,請求頭中的 Content-Type
欄位值為multipart/form-data
,在 Content-Type
中可能還附帶內容分隔符 boundary=----WebKitFormBoundary4Hsing01Izo2AHqv
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4Hsing01Izo2AHqv
複製程式碼
先上傳一個JS檔案,看看報文主體裡面的格式大概是這樣的,包含檔案資訊和檔案內容,有指定的分隔符包裹。
上傳檔案的時候是要區分文字檔案和二進位制檔案,文字檔案是要使用 utf8
編碼(HTML,CSS,JavaScript),二進位制檔案是要使用 binary
編碼(圖片,視訊,音訊)
根據內容分隔符解析上傳的圖片,並且寫入到檔案中,下面程式碼暫時只處理圖片格式的檔案。
function handleFile(req, res) {
const isFile = mime(req) === 'multipart/form-data'
if (hasBody(req) && isFile) {
var buffers = [];
req.on('data', function (chunk) {
buffers.push(chunk);
});
req.on('end', function () {
// 處理檔名
let requestBody = Buffer.concat(buffers).toString('binary');
let file = querystring.parse(requestBody, '\r\n', ': ')
let fileInfo = file['Content-Disposition']
fileInfo = Buffer.from(fileInfo, 'binary').toString()
let { filename } = querystring.parse(fileInfo, '; ', '=')
filename = filename.slice(1, -1)
filename = `./static/${filename}`
// 處理內容
let boundary = req.headers['content-type'].split('; ')[1].replace('boundary=', '');
let contentType = file['Content-Type']
if (!contentType.includes('image')) return
let upperBoundary = requestBody.indexOf(contentType) + contentType.length;
let shorterData = requestBody.substring(upperBoundary)
let binaryDataAlmost = shorterData.trim()
let binaryData = binaryDataAlmost.substring(0, binaryDataAlmost.indexOf(`--${boundary}--`))
// 寫入檔案
fs.writeFile(filename, binaryData, 'binary', (err) => {
if (err) {
console.log('上傳失敗')
} else {
console.log('上傳成功', filename)
}
})
});
}
}
複製程式碼
這就是所有處理POST請求的方式,你都學會了嗎?