談談form-data請求格式

最近一直都比較忙,堅持月月更新博客的計劃不得停止了,今天抽出點時間來講說最近項目中遇到的一個問題,有關request post請求格式中的multipart/form-data格式。javascript

引言

最近在參與一個項目過程當中遇到一個問題,相信大部分人都遇到過:html

在後端與前端約定好application/json格式傳遞數據時,由於後臺是go強類型語言,在定義api接口時,某些字段要求是整型類型,可是對於前端來講輸入框或者從url中的search取到的參數都是字符串,不得不進行前端類型轉換。前端

咋一看,對於接口參數比較少的api前端轉換沒有什麼,可是對於通常的交互複雜,參數比較多的接口,要對大部分參數進行類型轉換就是一種吃力不討好的活。好在後端同窗還支持另外一種的先後端數據交互格式,即multipart/form-data。經過該格式後端取到前端傳遞的數據就是數字了(即便前端傳遞的是字符串),而不像json格式獲取的是字符串。這樣,就不須要額外對前端獲取的數據進行特殊轉換了。下面就來講說form-data。java

form-data請求格式

multipart/form-data是基於post方法來傳遞數據的,而且其請求內容格式爲Content-Type: multipart/form-data,用來指定請求內容的數據編碼格式。另外,該格式會生成一個boundary字符串來分割請求頭與請求體的,具體的是以一個boundary=${boundary}來進行分割,僞碼以下:git

...
Content-Type: multipart/form-data; boundary=${boundary} 

--${boundary}
...
...

--${boundary}--

上面boundary=${boundary}以後就是請求體內容了,請求體內容各字段之間以--${boundary}來進行分割,以--${boundary}--來結束請求體內容。具體能夠參考下面例子:github

POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryyb1zYhTI38xpQxBK

------WebKitFormBoundaryyb1zYhTI38xpQxBK
Content-Disposition: form-data; name="city_id"

1

------WebKitFormBoundaryyb1zYhTI38xpQxBK
Content-Disposition: form-data; name="company_id"

2
------WebKitFormBoundaryyb1zYhTI38xpQxBK
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryyb1zYhTI38xpQxBK--

form-data格式通常是用來進行文件上傳的。使用表單上傳文件時,必須讓 ajax

表單的 enctype 等於 multipart/form-data,由於該值默認值爲 application/x-www-form-urlencoded

FormData對象

XMLHttpRequest Level 2添加了一個新的接口FormData。利用FormData對象,咱們能夠經過JavaScript用一些鍵值對來模擬一系列表單控件,咱們還能夠使用XMLHttpRequest的send()方法來異步的提交這個"表單"。chrome

var formData = new FormData();
formData.append("username", "Groucho");
formData.append("accountnum", 123456); 
fetch('/users', {
  method: 'POST',
  body: formData
})

上面建立了一個FormData對象,經過fetch進行ajax請求時,會自動爲其將其轉爲form-data格式,無需手動添加格式。json

對象轉FormData對象

對於FormDat對象,像上面那種形式能夠直接添加參數比較方便,可是對於對象或者嵌套對象:後端

let userObj = {userName: ’xxx', age: '21'}
 formData.append('user', userObj)

上面形式添加formData參數user,並不會獲取到其真正的內容,而是返回userObj的Object.prototype.toString.call(userObj)的值做爲user字段的值。

------WebKitFormBoundaryyb1zYhTI38xpQxBK
Content-Disposition: form-data; name="user"

[object Object]

遺憾的是,FormData對象沒有像JSON.stringify那樣的方法能批量將對象形式轉換爲對應的形式,formData而言是將對象的key轉換爲正確formData請求參數字段名,例如以下對象:

var obj = {
    a: '2', 
    b: {c: 'test'}, 
    c: [ 
        {id: 1, name: 'xx'}, 
        {id:2 ,name: 'yy', info: {d: 4} }
    ]
}

這樣轉換爲FormData對象時,其對應的key應該是下面這樣的:

a: 2
b[c]: test
c[][id]: 1
c[][name]: xx
c[][id]: 2
c[][name]: yy
c[][info][d]:4

這樣,就須要咱們本身手動來實現一個轉換數據函數,具體代碼以下:

function objectToFormData (obj, form, namespace) {
  const fd = form || new FormData();
  let formKey;
  
  for(var property in obj) {
      if(obj.hasOwnProperty(property)) {
        let key = Array.isArray(obj) ? '[]' : `[${property}]`;
        if(namespace) {
          formKey = namespace + key;
        } else {
          formKey = property;
        }
      
        // if the property is an object, but not a File, use recursivity.
        if(typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
          objectToFormData(obj[property], fd, formKey);
        } else {
          
          // if it's a string or a File object
          fd.append(formKey, obj[property]);
        }
        
      }
    }
  
  return fd;
    
}

這樣,就能夠將對象轉化爲對應的formData的格式了。

參考

一、四種常見的 POST 提交數據方式
二、轉換formdata參數格式
二、gist
三、fetch

相關文章
相關標籤/搜索