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

form-data與x-www-form-urlencoded的區(qū)別以及知識延伸

 更新時間:2023年11月30日 10:08:54   作者:代碼君.  
這篇文章主要給大家介紹了關(guān)于form-data與x-www-form-urlencoded的區(qū)別以及知識延伸,form-data和x-www-form-urlencoded都是HTTP請求中用于傳輸表單數(shù)據(jù)的編碼格式,需要的朋友可以參考下

一、前言

form-data和x-www-form-urlencoded,它們完整的表示是multipart/form-data和application/x-www-form-urlencoded。

為了方便,我們下面就用form-data和x-www-form-urlencoded表示。

兩者的區(qū)別,可謂是老生常談,隨便百度一下,也是有大堆資料??墒俏疫€想用一篇文章來總結(jié)一下,主要有兩點原因:

  • form-data和x-www-form-urlencoded雖然是基礎(chǔ),但卻很重要。而且最近在工作中,恰好遇到了這方便的坑。經(jīng)過一番研究,有了新的體悟,所以想要總結(jié)一下。
  • 文章內(nèi)容不只是比較兩個的區(qū)別,還會引申到HttpServletRequest,以及流不可重復(fù)讀等問題,幫助大家把相關(guān)的知識串起來。

好了,閑話少敘,我們進入正題。

二、form-data和x-www-form-urlencoded區(qū)別

form-data 和 x-www-form-urlencoded 都是表單請求的一種格式,主要區(qū)別有兩點。

編碼方式不同

我們先說x-www-form-urlencoded,它的編碼方式就隱藏在名字里:urlencoded??吹竭@個關(guān)鍵詞,你想到了什么?

沒錯,就是js中的encodeURI函數(shù),這個函數(shù)的功能,我相信大家并不陌生,我們就不在贅述了。

x-www-form-urlencoded就可以理解為被encodeURI編碼過的querystring。

比如,我們用x-www-form-urlencoded提交如下內(nèi)容:

name: 張三
age: 18

實際上,請求體會被編碼成如下格式:

name=%E5%BC%A0%E4%B8%89&age=18

這里需要強調(diào)一下,urlencoded這種格式并不是json格式,因此不能使用json庫將其轉(zhuǎn)成json。

接下里,我們來講form-data,完整的表示為multipart/form-data。

multipart/form-data這種格式,會把表單內(nèi)容分成多個部分,這也就是multipart的含義。

比如,同樣是上面的表單內(nèi)容,使用multipart/form-data格式,請求體將會類似下面這樣:

--AaB03x
Content-Disposition: form-data; name="name"
Content-Type: text/plain
張三
--AaB03x
Content-Disposition: form-data; name="age"
Content-Type: text/plain
18
--AaB03x--

接下來,我們詳細說一下其中的細節(jié)。

  • --AaB03x,boundary,也就是邊界,它就是分割表單不同part的分界線。AaB03x是一個隨機字符串,需要保證整個請求體都是用相同的boundary。
  • name="name"和name="age",這個很容易理解,就是每個part的名字,也就是字段名。
  • Content-Type,每個part的內(nèi)容類型,我們這里因為傳的是普通文本,所以內(nèi)容類型使用text/plain。
  • --boundary表示一個part的開始,--boundary--表示所有part都結(jié)束了。

除了上面的請求體,我們的請求頭需要向下面這樣設(shè)置:

Content-Type: multipart/form-data; boundary=AaB03x

這里關(guān)鍵的部分就是boundary=AaB03x,定義分界線的隨機數(shù)是什么,請求體中使用的隨機數(shù),需要和請求頭中定義的隨機數(shù)一致。

好了,這兩種編碼方式的基本格式,我們已經(jīng)講完了,你的第一感覺是什么,是不是覺得multipart/form-data的格式,要比x-www-form-urlencoded復(fù)雜的多。

沒錯,這正x-www-form-urlencoded的一大優(yōu)點——對于普通的文本內(nèi)容,x-www-form-urlencoded占用的字節(jié)更少。

支持的內(nèi)容類型不同

x-www-form-urlencoded只支持普通的文本內(nèi)容,而multipart/form-data的每一個part部分,都支持不同的Content-Type,比如圖片、音頻、視頻等。

比如,我們向表單里面添加一個圖片:

--AaB03x
Content-Disposition: form-data; name="name"
Content-Type: text/plain
張三
--AaB03x
Content-Disposition: form-data; name="age"
Content-Type: text/plain
18
--AaB03x
Content-Disposition: form-data; name="age"; filename="test.jpg"
Content-Type: image/jpeg
xxxxxxxx
--AaB03x--

和普通的文本內(nèi)容不同的是,我們增加一個filename參數(shù),來表示文件名稱;并且把Content-Type設(shè)置成image/jpeg,表明它的格式是圖片。

而且,你肯定也意識到了,multipart/form-data也不是json格式,也不能用json庫讀取它。

接下來,我們簡單總結(jié)一下兩者區(qū)別。

  • x-www-form-urlencoded,一種輕型表單,只支持普通文本,優(yōu)點是占用字節(jié)少。
  • multipart/form-data,會把內(nèi)容分成多個部分,每個部分都支持不同的格式,優(yōu)點是支持文件上傳,缺點是占用字節(jié)多。

三、HttpServeltRequest的getParameter

上面,我們已經(jīng)把multipart/form-data和x-www-form-urlencoded的基本區(qū)別,給大家講明白了。

接下來,我們來延伸一個內(nèi)容,就是HttpServletRequest的getParameter——這個我們幾乎每天都會接觸到的方法。

我之前一直以為,getParameter方法只能獲取請求地址上的參數(shù),比如:

/api/user?name=張三&age=18

直到最近,我才發(fā)現(xiàn)事情遠沒有這么簡單。這也是我為什么要單獨給大家引申這塊內(nèi)容的原因。

實際上,getParameter除了能獲取請求地址上的參數(shù),還能獲取表單中的參數(shù),包括x-www-form-urlencoded和form-data這兩種格式。

不知道,你有沒有好奇過,為什么getParameterMap返回的是Map<String, String[]>格式,除了請求地址上可能傳數(shù)組的情況,比如:

/api/user?name=張三&age=18&age=20

另外一個重要的原因,就是同一個參數(shù)名,除了可以出現(xiàn)在url地址上,還可以出現(xiàn)在請求體中,比如下面的post請求:

/api/user?name=張三&age=18

Content-Type: application/x-www-form-urlencoded

age=20&gender=male

此時age就有兩個值,所以返回的是數(shù)組,只是getParameter方法默認只返回數(shù)組的第一個元素而已。

public String getParameter(String name ) {
    handleQueryParameters();
    ArrayList<String> values = paramHashValues.get(name);
    if (values != null) {
        if(values.size() == 0) {
            return "";
        }
        return values.get(0);
    } else {
        return null;
    }
}

至此,你有沒有發(fā)現(xiàn)什么細思極恐的事情。在之前我一直排斥使用getParameterMap方法,原因就是我討厭處理數(shù)組。

相反,我更情愿使用getParameter。但現(xiàn)在看來,這種便利可能給我的代碼帶來bug——因為getParameter并不兼容參數(shù)傳數(shù)組的情況。

當(dāng)然,并不是說getParameter不能用,而是在用之前需要問下自己,這個參數(shù)是不是只會傳單個值。

這里還能總結(jié)一個經(jīng)驗:如果可以,不要用HttpServletRequest接收參數(shù),更好的做法是將請求參數(shù)封裝成一個類。

比如:

@Data
public class UserDTO {
    private String name;
    private List<Integer> age;
}

一方面,數(shù)據(jù)類型能夠傳達更多的信息,告訴我們這個參數(shù)可能傳遞一個數(shù)組;另一方便,Spring的data-binder能幫我處理好一切。

雖然,getParameter可以獲取x-www-form-urlencoded和form-data中參數(shù),但是它們還是有一些細微差別。

  • x-www-form-urlencoded:所有的參數(shù)都能被獲取
  • multipart/form-data:不能讀取帶有filename標(biāo)記的內(nèi)容
private void parseRequest(HttpServletRequest request) {
   try {
      Collection<Part> parts = request.getParts();
      this.multipartParameterNames = new LinkedHashSet<>(parts.size());
      MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<>(parts.size());
      for (Part part : parts) {
         String headerValue = part.getHeader(HttpHeaders.CONTENT_DISPOSITION);
         ContentDisposition disposition = ContentDisposition.parse(headerValue);
         String filename = disposition.getFilename();
         if (filename != null) {
            if (filename.startsWith("=?") && filename.endsWith("?=")) {
               filename = MimeDelegate.decode(filename);
            }
            files.add(part.getName(), new StandardMultipartFile(part, filename));
         }
         else {
             // 只有filename為空part,才會放到multipartParameterNames里面
            this.multipartParameterNames.add(part.getName());
         }
      }
      setMultipartFiles(files);
   }
   catch (Throwable ex) {
      handleParseFailure(ex);
   }
}

你是不是還好奇,getParameter取數(shù)組的第一個元素,到底是請求體中的參數(shù),還是url地址上的參數(shù)呢。

在寫這篇文章時,我本打算介紹一下這個細節(jié)。但是后來,理智告訴我放棄傳播這種無用的知識,因為它并不能指導(dǎo)我們寫出更好的代碼。相反可能會將我們引入錯誤的方向,比如下面的代碼:

Map<String, String[]> parameterMap = request.getParameterMap();
if (parameterMap.containsKey("age") && parameterMap.get("age").length > 1) {
    String age = parameterMap.get("age")[1];
}

你知道它想表達什么嗎?

好了,這一節(jié)的內(nèi)容細節(jié)有點多,如果你讀起來有點吃力,建議多讀幾遍。

四、流不可重復(fù)讀

流不可重復(fù)讀的問題,相信你一定遇到過。不過,你可能會疑問:我們今天講的內(nèi)容,跟這個問題有什么關(guān)系呢?

你別說,還真有,我最近就被這個問題坑了!

最近,我在工作中要開發(fā)一個接口代理的工具,需要對請求體進行一些處理。當(dāng)我測試x-www-form-urlencode格式時,死活獲取不到請求體。通過debug才發(fā)現(xiàn),原來是getParameter搞的鬼。

相信通過前面的介紹,你已經(jīng)知道原因:對于form-data和x-www-form-urlencoded這兩種格式,getParameter方法會從請求體中查詢參數(shù),既然是請求體,當(dāng)然會讀取InputStream,從而導(dǎo)致后續(xù)再讀取流的時候獲取不到任何內(nèi)容。

這個問題的一般解法,相信你已經(jīng)知道了,就是寫一個Filter對HttpServletRequest包裝,讀取InputStream中的數(shù)據(jù),并緩存到一個byte[]中。包裝類重寫getInputStream方法返回一個ByteArrayInputStream,從而解決流不同重復(fù)讀的問題。

上面的解法,網(wǎng)上的資料一搜一大堆,我就不貼代碼了。

我今天要給你介紹的是另外一種思路,這種思路只能部分解決由于getParameter或getParameterMap導(dǎo)致的流讀取的問題。

這里的部分解決,指的是如果你只是希望獲取請求地址上的參數(shù),那么這個方法能夠奏效——它不會讀取IO流。

這里的思路就是解析querystring。

可以通過request.getQueryString方法獲取查詢字符串,接下來的關(guān)鍵,就是如何方便的處理它。

推薦兩個工具類,第一個是UriComponentsBuilder:

String queryString = request.getQueryString();

MultiValueMap<String, String> queryParams = UriComponentsBuilder.fromUriString("?" + queryString)
        .build()
        .getQueryParams();

這個類是Spring提供的,對于SpringBoot用戶來說,開箱即用。

第二個是URLEncodedUtils:

String queryString = request.getQueryString();

List<NameValuePair> nameValuePairs = URLEncodedUtils.parse(queryString, StandardCharsets.UTF_8);

Map<String, List<String>> queryParams = nameValuePairs.stream()
        .collect(Collectors.groupingBy(NameValuePair::getName))
        .entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream()
        .map(NameValuePair::getValue).collect(Collectors.toList())));

這個類是httpclient提供的工具類,需要引入maven包:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>${version}</version>
</dependency>

這兩個工具類,能很好的幫助我們處理querystring,別再傻傻的用split函數(shù)分割了。

五、總結(jié)

“溫故而知新,可以為師矣”。

這篇文章從form-data和x-www-form-urlencoded的區(qū)別為起點,一步步展開,介紹了HttpServletRequest的getParameter方法,及其導(dǎo)致的流不可重復(fù)讀的問題。

到此這篇關(guān)于form-data與x-www-form-urlencoded的區(qū)別以及知識延伸的文章就介紹到這了,更多相關(guān)form-data與x-www-form-urlencoded區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java項目實現(xiàn)猜拳小游戲

    java項目實現(xiàn)猜拳小游戲

    這篇文章主要為大家詳細介紹了java項目實現(xiàn)猜拳小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • RabbitMQ的安裝集群、鏡像隊列配置規(guī)劃

    RabbitMQ的安裝集群、鏡像隊列配置規(guī)劃

    RabbitMQ 集群是一個或多個節(jié)點的邏輯分組,每個節(jié)點共享用戶、虛擬主機、隊列、流、交換機、綁定、運行時參數(shù)和其他分布式狀態(tài),本文給大家介紹RabbitMQ的安裝集群、鏡像隊列配置規(guī)劃,感興趣的朋友一起看看吧
    2025-09-09
  • Java判斷絕對值的方法總結(jié)

    Java判斷絕對值的方法總結(jié)

    在本文中作者為大家分享了兩種Java判斷絕對值的方法,對此有興趣的朋友可以和小編一起學(xué)習(xí)下。
    2018-02-02
  • Java中如何取出String字符串括號中的內(nèi)容

    Java中如何取出String字符串括號中的內(nèi)容

    這篇文章主要介紹了Java中如何取出String字符串括號中的內(nèi)容問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Mybatis查詢語句結(jié)果集的總結(jié)大全

    Mybatis查詢語句結(jié)果集的總結(jié)大全

    這篇文章主要給大家總結(jié)介紹了關(guān)于Mybatis查詢語句結(jié)果集的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • Springboot項目因為kackson版本問題啟動報錯解決方案

    Springboot項目因為kackson版本問題啟動報錯解決方案

    這篇文章主要介紹了Springboot項目因為kackson版本問題啟動報錯解決方案,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • 解析JAVA深度克隆與淺度克隆的區(qū)別詳解

    解析JAVA深度克隆與淺度克隆的區(qū)別詳解

    本篇文章是對JAVA深度克隆與淺度克隆的區(qū)別進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • Java實現(xiàn)文件上傳的兩種方法(uploadify和Spring)

    Java實現(xiàn)文件上傳的兩種方法(uploadify和Spring)

    這篇文章主要為大家詳細介紹了Java實現(xiàn)文件上傳的兩種方法,uploadify和Spring實現(xiàn)文件上傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • MyBatis延遲加載與立即加載案例教程

    MyBatis延遲加載與立即加載案例教程

    這篇文章主要介紹了MyBatis延遲加載與立即加載案例教程,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • 淺談java8中map的新方法--replace

    淺談java8中map的新方法--replace

    下面小編就為大家?guī)硪黄獪\談java8中map的新方法--replace。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-10-10

最新評論

灵川县| 永丰县| 凌海市| 临沧市| 广昌县| 苗栗县| 密云县| 甘肃省| 永州市| 颍上县| 昌黎县| 彝良县| 马山县| 同江市| 双江| 融水| 毕节市| 郑州市| 卫辉市| 临漳县| 正阳县| 阿瓦提县| 鄂温| 红原县| 石景山区| 福安市| 灵台县| 正定县| 宁陵县| 榆林市| 神池县| 衢州市| 通海县| 鄢陵县| 临漳县| 闸北区| 厦门市| 长汀县| 五大连池市| 康定县| 临湘市|