最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解axios在node.js中的post使用

 更新時間:2017年04月27日 09:57:20   作者:chux0519  
最近因?yàn)楣ぷ鞯脑蛟趯W(xué)習(xí)使用網(wǎng)絡(luò)請求庫,因?yàn)檫@個項(xiàng)目用的是Promise,所以就選擇了axios,下面這篇文章主要給大家介紹了關(guān)于axios在node.js中的post使用的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起學(xué)習(xí)學(xué)習(xí)吧。

前言:

最近因?yàn)樽龅臇|西需要用到網(wǎng)絡(luò)請求庫,之前接觸過的只有request,很強(qiáng)大好用。但是這個項(xiàng)目中需要用到Promise,我又不想重新封裝,于是選擇了另一款庫axios。

在node中,axios的get請求加上原生支持的Promise語法使用起來很方便,很絲滑,但是后面碰到了一個需求,就是要向另一個服務(wù)器post數(shù)據(jù),并且這個數(shù)據(jù)是以form-data的形式post過去的,這時,問題就出現(xiàn)了。

問題:

當(dāng)我想在node中使用axios以post的方式發(fā)送一張圖片給某個server時,最先我是嘗試這樣做:

方案一

 let data = fs.createReadStream(__dirname + '/test.jpg')
 axios.post(url,{media:data,type:"image"})
 .then(function (response) {
 console.log(response.data);
 })
 .catch(function (error) {
 console.log(error);
 })

事實(shí)證明,這樣做是完全沒有用的,我嘗試向另一個服務(wù)器poststream,返回的總是錯誤。然而,如果我使用request,下面這樣的代碼是完全沒有問題的:

方案二

 let data = fs.createReadStream(__dirname + '/test.jpg')
 let form = {
 type:"image",
 media:data
 }
 
 request.post({url:url,formData:form},(err,res,body)=>{
 if(err) console.log(err)
 console.log(body)
 })

探索:

于是,我陷入了思考,WTF!!

我打算簡單的寫一個服務(wù)器,用于打印HTTP請求,然后查看區(qū)別(別問我為什么不用抓包工具,任性?。?,代碼呼之欲出:

 import Koa from 'koa'

 const app = new Koa()

 app.use(ctx=>{
 console.log("===============================================")
 console.log(ctx.request)
 console.log("===============================================")
 ctx.body = {foo:"bar"}
 })

 app.listen(3000,()=>{
 console.log("listening on 3000 port")
 })

此時,將url設(shè)置為:http://127.0.0.1:3000/,再分別執(zhí)行方案一和方案二 這時打印出了這樣的結(jié)果:

 listening on 3000 port
 ===============================================
 { method: 'POST',
 url: '/',
 header: 
 { accept: 'application/json, text/plain, */*',
 'content-type': 'application/json;charset=utf-8',
 'user-agent': 'axios/0.14.0',
 'content-length': '587',
 host: '127.0.0.1:3000',
 connection: 'close' } }
 ===============================================
 ===============================================
 { method: 'POST',
 url: '/',
 header: 
 { host: '127.0.0.1:3000',
 'content-type': 'multipart/form-data; boundary=--------------------------949095406788084443059291',
 'content-length': '186610',
 connection: 'close' } }
 ===============================================

- 上面的是方案一,下面的是方案二

這時可以看出,方案一和二的差別最明顯的是content-type,是的,這也是決定了方案一不可行的因素。 既然是content-type導(dǎo)致的,那么方案一PLUS就比較明了了,查閱axios的文檔后,我決定手動設(shè)置content-type,于是乎:

 let data = fs.createReadStream(__dirname + '/test.jpg')
 let header = {
 'content-type': 'multipart/form-data'
 }
 axios.post(url,{media:data,type:"image"},{headers:header})
 .then(function (response) {
 console.log(response.data);
 })
 .catch(function (error) {
 console.log(error);
 })

- 這時,請求是這樣的:

 ===============================
 { method: 'POST',
 url: '/',
 header: 
 { accept: 'application/json, text/plain, */*',
  'content-type': 'multipart/form-data',
  'user-agent': 'axios/0.14.0',
  'content-length': '587',
  host: '127.0.0.1:3000',
  connection: 'close' } }
 ================================

貌似差別不大,但我先試著往服務(wù)器post數(shù)據(jù)時,仍然返回錯誤。實(shí)際上這時候沒有boundary,文件其實(shí)并沒有被綁定上去,所以現(xiàn)在仍然沒有解決問題。至于boundary,這里有個鏈接非常能說明問題。

到這里,我們就要耐下心來好好思考了,區(qū)別就在于,request中能夠設(shè)置正確的請求頭,那么它是怎么辦到的呢,于是我開始翻看request的源碼,發(fā)現(xiàn)了這一段:

 if (options.formData) {
 var formData = options.formData
 var requestForm = self.form()
 var appendFormValue = function (key, value) {
 if (value && value.hasOwnProperty('value') && value.hasOwnProperty('options')) {
 requestForm.append(key, value.value, value.options)
 } else {
 requestForm.append(key, value)
 }
 }
 for (var formKey in formData) {
 if (formData.hasOwnProperty(formKey)) {
 var formValue = formData[formKey]
 if (formValue instanceof Array) {
 for (var j = 0; j < formValue.length; j++) {
 appendFormValue(formKey, formValue[j])
 }
 } else {
 appendFormValue(formKey, formValue)
 }
 }
 }
 }

這一段是request在初始化參數(shù)中的formData,其中調(diào)用了它自身的form()方法,追蹤這個函數(shù):

 Request.prototype.form = function (form) {
 var self = this
 if (form) {
 if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) {
 self.setHeader('content-type', 'application/x-www-form-urlencoded')
 }
 self.body = (typeof form === 'string')
 ? self._qs.rfc3986(form.toString('utf8'))
 : self._qs.stringify(form).toString('utf8')
 return self
 }
 // create form-data object
 self._form = new FormData()
 self._form.on('error', function(err) {
 err.message = 'form-data: ' + err.message
 self.emit('error', err)
 self.abort()
 })
 return self._form
 }

發(fā)現(xiàn)了request調(diào)用了另一個庫form-data,先通過self.form()創(chuàng)建出一個formData對象,再遍歷options里的formData項(xiàng),遞歸地將內(nèi)容通過formData的append方法放進(jìn)去,也就是說是formData實(shí)現(xiàn)了post文件,于是乎,我在axios中插入formData,形成了方案三:

方案三:

 let data = fs.createReadStream(__dirname + '/test.jpg')
 let form = new FormData()
 form.append('type','image')
 form.append('media',data,'test.jpg')

 axios.post(url,form).then((response)=>{
 console.log(response.data)
 })
 .catch(e=>{console.log(e)})

但是,事實(shí)告訴我,我還是悲劇了,請求打印出來是這樣的:

 ===============================================
 { method: 'POST',
 url: '/',
 header: 
 { accept: 'application/json, text/plain, */*',
 'content-type': 'application/x-www-form-urlencoded',
 'user-agent': 'axios/0.14.0',
 host: '127.0.0.1:3000',
 connection: 'close',
 'transfer-encoding': 'chunked' } }
 ===============================================

再次content-type還是不對,于是我再去翻axios的文檔和issue,發(fā)現(xiàn),默認(rèn)設(shè)置的content-type就是application/x-www-form-urlencoded,于是我判斷,一定還是要手動設(shè)置headers的

于是,基于方案三,我又添加了和改動了這兩行形成了方案四:

方案四

 let header = {
 'content-type': 'multipart/form-data'
 }

 axios.post(url,form,{headers:header}).then((response)=>{
 console.log(response.data)
 })

但結(jié)果還是不理想,直接設(shè)置content-type是不行的,因?yàn)橐獙⒋l(fā)送文件綁定,就一定會有boundary出現(xiàn),另外在方案三和方案四的請求中,出現(xiàn)了transfer-encoding這個值,關(guān)于這個chunked,可以參考MDN這篇文章

一邊google一邊看文檔的我,發(fā)現(xiàn)formData的文檔中出現(xiàn)過form.getHeaders()的寫法,于是方案五出現(xiàn)了:

方案五

 let data = fs.createReadStream(__dirname + '/test.jpg')
 let form = new FormData()
 form.append('type','image')
 form.append('media',data,'test.jpg')

 axios.post(url,form,{headers:form.getHeaders()}).then((response)=>{
 console.log(response.data)
 })
 .catch(e=>{console.log(e)})

但是結(jié)果表明,這樣還是不行,現(xiàn)在的請求是這樣:

 ===============================================
 { method: 'POST',
 url: '/',
 header: 
  { accept: 'application/json, text/plain, */*',
 'content-type': 'multipart/form-data; boundary=--------------------------171407872885673042671614',
 'user-agent': 'axios/0.14.0',
 host: '127.0.0.1:3000',
 connection: 'close',
 'transfer-encoding': 'chunked' } }
 ===============================================

但是我目前項(xiàng)目需求是,不使用chunked而采用content-length的方法來傳輸,這意味著,我要想辦法搞到form的長度

在成功案例中,使用requests,于是我翻看了部分源碼: 在request/request.js里出現(xiàn)了

 function setContentLength () {
 if (isTypedArray(self.body)) {
  self.body = new Buffer(self.body)
 }
 
 if (!self.hasHeader('content-length')) {
  var length
  if (typeof self.body === 'string') {
 length = Buffer.byteLength(self.body)
  }
  else if (Array.isArray(self.body)) {
 length = self.body.reduce(function (a, b) {return a + b.length}, 0)
  }
  else {
 length = self.body.length
  }
 
  if (length) {
 self.setHeader('content-length', length)
  } else {
 self.emit('error', new Error('Argument error, options.body.'))
  }
 }
 }

它采用Buffer來計(jì)算長度,然后添加到headers中去

然后看看在axios里是如何做的: axios/lib/adapters/http.js里出現(xiàn)了

 if (data && !utils.isStream(data)) {
  if (utils.isArrayBuffer(data)) {
 data = new Buffer(new Uint8Array(data));
  } else if (utils.isString(data)) {
 data = new Buffer(data, 'utf-8');
  } else {
 return reject(createError(
  'Data after transformation must be a string, an ArrayBuffer, or a Stream',
  config
 ));
  }
 
  // Add Content-Length header if data exists
  headers['Content-Length'] = data.length;
 }

下文并沒有出現(xiàn)else,所以,當(dāng)data是stream的時候,并沒有自動設(shè)置content-length

所以,我需要在formData.getHeaders()后,再添加一個content-length的key

想要計(jì)算長度,自然想到去看看源碼,于是在form-data/lib/form_data.js中出現(xiàn)了驚喜:

 FormData.prototype.getLength = function(cb) {
 var knownLength = this._overheadLength + this._valueLength;
 
 if (this._streams.length) {
 knownLength += this._lastBoundary().length;
 }
 
 if (!this._valuesToMeasure.length) {
 process.nextTick(cb.bind(this, null, knownLength));
 return;
 }
 
 asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function(err, values) {
 if (err) {
  cb(err);
  return;
 }
 
 values.forEach(function(length) {
  knownLength += length;
 });
 
 cb(null, knownLength);
 });
 };

formData已經(jīng)封裝好了得到長度的方法,只不過它是異步的,不過沒關(guān)系,在實(shí)際項(xiàng)目中,可以將它手動Promise化。最終方案的代碼也就自然出現(xiàn)了:

方案六:

 let data = fs.createReadStream(__dirname + '/test.jpg')
 let form = new FormData()
 form.append('type','image')
 form.append('media',data,'test.jpg')
 form.getLength((err,length)=>{
 if(err) console.log(err)
 let headers = Object.assign({'Content-Length':length},form.getHeaders())
 axios.post(url,form,{headers:headers}).then((response)=>{
 console.log(response.data)
 })
 .catch(e=>{console.log(e)})
 })

這時的請求打印后是這樣的:

 ===============================================
 { method: 'POST',
 url: '/',
 header: 
 { accept: 'application/json, text/plain, */*',
 'content-type': 'multipart/form-data; boundary=--------------------------424584867554529984619649',
 'content-length': '186610',
 'user-agent': 'axios/0.14.0',
 host: '127.0.0.1:3000',
 connection: 'close' } }
 ===============================================

事實(shí)證明它是可以工作的。

更進(jìn)一步,我們把異步代碼Promise一下,得到最終方案:

最終方案

 let data = fs.createReadStream(__dirname + '/test.jpg')
 let form = new FormData()
 form.append('type','image')
 form.append('media',data,'test.jpg')

 let getHeaders = (form=>{
 return new Promise((resolve,reject)=>{
 form.getLength((err,length)=>{
 if(err) reject(err)
 let headers = Object.assign({'Content-Length':length},form.getHeaders())
 resolve(headers)
 })
 })
 })

 getHeaders(form)
 .then(headers=>{
 return axios.post(url,form,{headers:headers})
 })
 .then((response)=>{
 console.log(response.data)
 })
 .catch(e=>{console.log(e)})

總結(jié)

得到一個結(jié)論,多多看issue,多多看源碼,多多了解基礎(chǔ)知識(HTTP協(xié)議),對于問題的解決十分重要。最后這一套的實(shí)驗(yàn)代碼放在github上了,需要研究研究的同學(xué)們可以看看:axios-request或者下載到本地學(xué)習(xí)

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • 關(guān)于npm主版本升級及其相關(guān)知識點(diǎn)總結(jié)

    關(guān)于npm主版本升級及其相關(guān)知識點(diǎn)總結(jié)

    npm是Node.js默認(rèn)的包管理器,以javascript?編寫的軟件包管理系統(tǒng)用于分享和使用代碼,下面這篇文章主要給大家介紹了關(guān)于npm主版本升級及其相關(guān)知識點(diǎn)總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • nvm如何查看node版本

    nvm如何查看node版本

    文章介紹了如何使用nvm查看Node.js版本,并解決了由于舊淘寶鏡像廢棄導(dǎo)致的命令無效問題,解決方法是在nvm的setting文件中添加特定代碼
    2024-12-12
  • node.js實(shí)現(xiàn)快速截圖

    node.js實(shí)現(xiàn)快速截圖

    本文給大家匯總介紹了幾種node.js實(shí)現(xiàn)快速截屏的方法,這里分享給大家,希望大家能夠喜歡。
    2016-08-08
  • Node.js web 應(yīng)用如何封裝到Docker容器中

    Node.js web 應(yīng)用如何封裝到Docker容器中

    這篇文章主要介紹了Node.js web 應(yīng)用如何封裝到Docker容器中,幫助大家更好的學(xué)習(xí)node.js和使用docker容器,感興趣的朋友可以了解下
    2020-09-09
  • 阿里大于短信驗(yàn)證碼node koa2的實(shí)現(xiàn)代碼(最新)

    阿里大于短信驗(yàn)證碼node koa2的實(shí)現(xiàn)代碼(最新)

    本文給大家分享一個最新版阿里大于短信驗(yàn)證碼node koa2的實(shí)現(xiàn)代碼及注意事項(xiàng),需要的朋友參考下吧
    2017-09-09
  • 使用socket.io實(shí)現(xiàn)簡單聊天室案例

    使用socket.io實(shí)現(xiàn)簡單聊天室案例

    這篇文章主要介紹了使用socket.io實(shí)現(xiàn)簡單聊天室案例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Express中全局中間件與局部中間件的使用

    Express中全局中間件與局部中間件的使用

    本文介紹了Express框架中的全局中間件與局部中間件,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • 基于 Docker 開發(fā) NodeJS 應(yīng)用

    基于 Docker 開發(fā) NodeJS 應(yīng)用

    這是兩篇文章的第一篇。本文涵蓋了有關(guān)在使用 Express 框架開發(fā)一個Node應(yīng)用時,用Docker 替代 Vagrant 的比較詳細(xì)的教程, 應(yīng)用將使用 connect-redis 中間件將會話信息持久化到Redis中. 第二篇文章將介紹到將這個開發(fā)的設(shè)置產(chǎn)品化.
    2014-07-07
  • koa router 多文件引入的方法示例

    koa router 多文件引入的方法示例

    這篇文章主要介紹了koa router 多文件引入的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-05-05
  • 深入剖析Node.js cluster模塊

    深入剖析Node.js cluster模塊

    Node的單線程設(shè)計(jì)已經(jīng)沒法更充分的"壓榨"機(jī)器性能了,Node新增了一個內(nèi)置模塊cluster,它可以通過一個父進(jìn)程管理一坨子進(jìn)程的方式來實(shí)現(xiàn)集群的功能,這篇文章主要介紹了深入剖析Node.js cluster模塊,感興趣的小伙伴們可以參考一下
    2018-05-05

最新評論

广水市| 洞口县| 大厂| 滨海县| 宁河县| 贺州市| 漳平市| 富平县| 科技| 鄂托克前旗| 保定市| 扬中市| 舒城县| 滕州市| 芦山县| 昌宁县| 英山县| 上杭县| 迁安市| 东乡族自治县| 青龙| 定边县| 贞丰县| 黑水县| 若尔盖县| 深水埗区| 晋宁县| 长沙市| 任丘市| 晴隆县| 宜春市| 白山市| 北碚区| 滕州市| 澜沧| 新密市| 湟中县| 德安县| 宜兰县| 大庆市| 大厂|