Fetch API與POST請求那些事
- 2020 年 3 月 7 日
- 筆記
簡述
相信不少前端開發童鞋與後端聯調介面時,都會碰到前端明明已經傳了參數,後端童鞋卻說沒有收到,尤其是post
請求,遇到的非常多。本文以node.js
作為服務端語言,借用express
框架,簡要分析客戶端發送post
請求的四種方式以及服務端如何接收。本文客戶端請求沒有藉助第三方ajax
庫,採用的是Fetch API
,雖然瀏覽器兼容性有點問題,但是用法簡潔靈活,以後可能會是一個趨勢。在說post
請求之前,先簡要概述下Fetch API
。
Fetch API
Fetch API
提供了一個獲取資源的介面(包括跨域請求),提供了更強大和靈活的功能集。未來可能是XMLHttpRequest
的一種替代方案。去年GitHub
程式碼去jQuery
重構時,就使用Fetch API
替代jQuery
的ajax
,畢竟目前JavaScript
很多原生語法都進行了大量精簡,比如DOM
操作API
、http
請求fetch
、es6+
等。今天的axios
可能就是明日的jQuery
!
簡單的實例
Fetch API
主要暴露了三個介面一個方法。
- 三個介面
Request
(資源請求)Response
(請求的響應)Headers
(Request/Response
頭部資訊)
- 一個方法
fetch()
(獲取資源調用的方法)
// 實例化一個Request實例 // 第一個參數一般指資源路徑 // 第二個參數可以理解為請求的配置項,包含頭部資訊和http請求一些關鍵配置(請求類型、參數...) let requestInstance = new Request('/hello', { method: 'post', headers: { 'Content-Type': 'application/json;charset=utf-8' }, body: '{"hello": "world"}' }) // fetch方法參數同Request實例 // 第一個參數為url或者Request實例 // 第二個參數為請求配置項 fetch(requestInstance).then(response => { // 返回的是一個Response的實例 // 調用Response實例的序列化方法,序列化成json,返回值是一個promise // 序列化方法有 json,text,formData,blob,arrayBuffer,redirct let result = response.json() result.then(res => { consolee.log(res) }) })
有意思的特性
Fetch API
添加了一個實驗性的功能,支援客戶端手動取消http
請求了,這個比較有意思,因為之前的ajax
貌似都不支援手動取消。藉助AbortSignal
介面,可以通過AbortController
實例化一個控制器,將實例的siginal
當做請求的配置項,傳遞到服務端,客戶端可以通過AbortController
實例的abort
方法,來終止當前的http
請求,示例程式碼如下:
<template> <button id='btn'>中止請求</button> </template> <script> // 實例化controller var controller = new AbortController() // 獲取實例的signal介面 var signal = controller.signal let btn = document.getElementById('btn') // 點擊按鈕,中止請求 btn.addEventListener('click', e => { controller.abort() }) // json方式提交數據 const url = 'http://192.168.43.216:3000' // 將signal介面放到請求配置項中 let testRequest = new Request(url + '/test', { method: 'post', headers: { 'Content-Type': 'application/json;charset=utf-8;' }, body: '{"foo":"bar"}', signal }) fetch(testRequest).then(response => { let result = response.text() result.then(res => { console.log(res) }) }) </script>
post請求四種傳參方式
本文所說的前端傳遞數據格式相對於主流的ajax
函數庫有一定區別,一般的ajax
函數庫為了方便用戶使用,都會對數據進行二次封裝。本文主要說原始的數據格式交互,具體ajax
庫的使用,還是以官方文檔為準。
請求頭(Request Headers
)的實體Content-Type
用於指示資源的MIME
類型,即客戶端傳遞消息的格式;響應頭中Content-Type
用於指示服務端返回消息的格式。所以在http
請求中,我們可以從報文中的Content-Type
屬性來判斷客戶端-服務端消息傳遞的格式。
JSON提交
JSON
是常用的一種前後端數據接收格式。前端傳遞的是鍵值對數據,即對象(Object
)。採用JSON
傳遞參數,請求頭Content-Type
為application/json;charset=utf-8
,其中charset
為採用的字符集。
注意點:
- 既然為JSON提交,就要對參數進行序列化,即
JSON.stringify(params)
,否則傳遞到服務端的參數可能是[Object object]
- 服務端(
node.js
)是以流的方式進行接收,接收完是一個JSON
字元串,調用JSON.parse(params)
可以對參數進行序列化
示例程式碼
客戶端:
const url = 'http://192.168.43.216:3000' let testRequest = new Request(url + '/test', { method: 'post', headers: { 'Content-Type': 'application/json;charset=utf-8;' }, body: JSON.stringify({a: 1}) }) fetch(testRequest).then(response => { let result = response.text() result.then(res => { console.log(res) }) })
服務端:
router.post('/test', (req, res, next) => { let data = '' req.on('data', chunk => { data += chunk }) req.on('end', () => { // 將JSON字元串解析成對象 data = JSON.parse(data) res.send(data) }) })
請求資訊:
請求頭提交
在實際開發中,遇到過不少後端開發,喜歡吧請求參數放在請求頭,類似於get
請求,即請求的參數是拼接在請求地址後面。個人覺得這種傳參方式並不好,一般瀏覽器對URL
長度是有限制的,以Chrome
為例,URL
最大長度正在7700
個字元左右,對於post
請求來說,最好參數還是放在body
中。
注意點
- 客戶端請求參數拼接在
url
後,在?
後,鍵值對寫法a=1
,多個鍵值對之間通過連接符&
連接 - 服務端能夠在
request
對象中,通過request.query
直接進行接收 - 由於參數是拼接在
url
後面,所以請求頭Content-Type
無需設置
示例程式碼
客戶端:
let queryStringRequest = new Request(`${url}/querystring?a=1&b=2`, { method: 'post' }) fetch(queryStringRequest).then(response => { let result = response.json() result.then(res => { console.log(res) }) })
服務端:
router.post('/querystring', (req, res, next) => { res.send(req.query) })
請求資訊:
普通表單提交
表單提交的方式有兩種,一種是普通的表單提交,另外一種是通過FormData
進行提交(主要應用在文件上傳)。單純的表單提交,與上述兩種參數格式上還是存在一定的差別的,主要體現在以下幾個方面。
Content-Type
表單提交Request Headers
的Content-Type
為application/x-www-form-urlencoded;charset=utf-8
。- 參數
表單提交參數是放在body
中,感覺是JSON
和請求頭提交的合體。參數位置與JSON
提交相同,參數格式與請求頭提交一致
示例程式碼
客戶端:
let formRequest = new Request(url + '/form', { method: 'post', headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;' }, body: 'a=1&b=2' }) fetch(formRequest).then(response => { let result = response.json() result.then(res => { console.log(res) }) })
服務端:
const fs = require('fs') router.post('/form', (req, res, next) => { let data = '' req.on('data', chunk => { data += chunk }) req.on('end', () => { data = decodeURI(data) // 將a=1&b=2解析成{a: 1, b: 2} let dataObj = querystring.parse(data) res.send(dataObj) }) })
請求資訊:
FormData提交 (文件上傳)
通常我們在進行文件上傳時,都會採用表單提交。參數放在body
中,只不過格式與普通的有差別,具體如下:
- 參數需要放在
FormData
的實例中,通過append
進行參數的添加 - 請求頭
Content-Type
為multipart/formdata
示例程式碼
客戶端:
<template> <input type="file" id="uploadFile"> </template> <script> let $input = document.getElementById('uploadFile') // 監聽文件上傳 $input.addEventListener('change', e => { let file = e.target.files[0] handleUploadFile(file) }) function handleUploadFile (file) { let bean = new FormData() bean.append('file', file) bean.append('hello', 'world') let uploadFileRequest = new Request(`${url}/upload`, { method: 'post', headers: { 'Content-Type': 'multipart/formdata' }, body: bean }) fetch(uploadFileRequest).then(response => { let result = response.text() result.then(res => { console.log(res) }) }) } </script>
服務端:
router.post('/upload', (req, res, next) => { let data = [] let size = 0 req.on('data', chunk => { data.push(chunk) size += chunk.length }) let rems = [] req.on('end', () => { let buffer = Buffer.concat(data, size) for (let i = 0; i < buffer.length; i++) { var v = buffer[i]; var v2 = buffer[i+1]; if(v==13 && v2==10){ rems.push(i); } } // 圖片資訊 var picmsg_1 = buffer.slice(rems[0]+2,rems[1]).toString(); var filename = picmsg_1.match(/filename=".*"/g)[0].split('"')[1]; // 圖片數據 var nbuf = buffer.slice(rems[3]+2,rems[rems.length-2]); var path = './static/'+filename; fs.writeFileSync(path , nbuf); res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8'}); res.end('<div id="path">'+path+'</div>'); }) })
請求資訊:
小結
post
請求向服務端提交參數,一般情況下都是放在body
中,但是從上文列舉的幾種傳參方式,仍然可以放在請求頭中傳遞,服務端對於在請求頭中傳遞的參數的處理和get
請求保持一致。此外,從node.js
接收的參數來看,除了放在請求頭中能夠直接獲取外,其餘三種請求方式都是以位元組流的方式傳遞到服務端的。熟悉post
請求的幾種傳參方式,有助於我們和後端同學進行介面聯調。