最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

由x-www-form-urlencoded引發(fā)的接口對(duì)接失敗

2023-03-19 17:52 作者:程序員-王堅(jiān)  | 我要投稿

問題發(fā)生

作者:打碼日記

這周正在寫代碼,突然,旁邊小哥問我個(gè)問題...

  • 小哥:我這有個(gè)接口,自己調(diào)用沒有問題,但別人調(diào)用就不行,這種問題該如何排查?

  • 我:抓下包看看呢...

  • 小哥:是這樣使用tcpdump嗎?

  • 我:是的

待小哥抓到包后,使用wireshark打開,并找到了相應(yīng)的請(qǐng)求,類似如下:

然后我讓小哥將這個(gè)請(qǐng)求,使用curl發(fā)一個(gè)同樣的請(qǐng)求,看能不能復(fù)現(xiàn)這個(gè)錯(cuò)誤,如下:

$ curl -X POST localhost:80/api \ ? ? ?-H 'Content-Type: application/x-www-form-urlencoded' \ ? ? ?-d 'eyJvcmRlcl9pZCI6MTIzNDU2Nzg5MDIxNDN9Cg=='

命令執(zhí)行之后,重現(xiàn)了調(diào)用方一樣的接口報(bào)錯(cuò)。

然后抓包小哥自己的正確請(qǐng)求是這樣的:

這里很容易發(fā)現(xiàn),別人調(diào)不通接口,小哥能調(diào)通,原因是別人的請(qǐng)求體里面缺失data=這一段??

先不管為什么缺這個(gè)會(huì)報(bào)錯(cuò),這里展示了一個(gè)實(shí)用技巧,對(duì)于http接口來說,排查這種接口調(diào)用差異問題,最直接高效的方法,就是對(duì)比正確調(diào)用與錯(cuò)誤調(diào)用的數(shù)據(jù)包!

問題解決#

那么接下來,就是研究為什么報(bào)錯(cuò)了,看看服務(wù)端的處理代碼,大概如下:

public JsonObject parseRequest(HttpServletRequest request, Charset charset) throws IOException { ? ? ?String base64Str = request.getParameter("data"); ? ? ?if (base64Str == null) { ? ? ? ? ? ?try (InputStream is = request.getInputStream()) { ? ? ? ? ? ? ? ? ?base64Str = StreamUtils.copyToString(is, charset); ? ? ? ? ? ?} ? ? ?} ? ? ?byte[] jsonBytes = Base64.getDecoder().decode(base64Str); ? ? ?return new Gson().toJsonTree(new String(jsonBytes, charset)).getAsJsonObject(); }

這個(gè)邏輯很簡(jiǎn)單,如下:

  1. 先從data參數(shù)中取數(shù)據(jù)。

  2. 若沒有再從請(qǐng)求體中拿。

  3. 然后base64解碼。

  4. 最后轉(zhuǎn)json對(duì)象。

我們接口基本都這樣,使用base64將數(shù)據(jù)包了一層,許多年過去了,具體原因不詳,不深究??

從上面處理邏輯看,按道理小哥的調(diào)用方式與別人的調(diào)用方式都是支持的,理論上來說,小哥的調(diào)用方式會(huì)命中request.getParameter,而別人的調(diào)用方式會(huì)命中request.getInputStream(),那為啥別人的調(diào)用方式不行?

小哥又調(diào)試了下上述服務(wù)端代碼,發(fā)現(xiàn)使用別人的調(diào)用方式時(shí),從request.getInputStream()中讀不到數(shù)據(jù)??

我在小哥旁邊,提示將ContentType改成text/plain試試,curl命令改成這樣:

$ curl -X POST localhost:80/api \ ? ? ?-H 'Content-Type: text/plain' \ ? ? ?-d 'eyJvcmRlcl9pZCI6MTIzNDU2Nzg5MDIxNDN9Cg=='

執(zhí)行這條命令后,接口返回了正確結(jié)果??

那為什么會(huì)這樣呢??????

ContentType指的是什么?#

首先來看看ContentType指的是什么,看2個(gè)例子

  1. 如果ContentType是application/x-www-form-urlencoded時(shí),請(qǐng)求可能是這樣的:

  1. 如果ContentType是application/json時(shí),請(qǐng)求可能是這樣的:

  1. 如果ContentType是application/xml時(shí),請(qǐng)求可能是這樣的:

不難發(fā)現(xiàn),ContentType這個(gè)請(qǐng)求頭的作用是,指定請(qǐng)求體的數(shù)據(jù)格式。比如application/x-www-form-urlencoded表示請(qǐng)求體是key=value格式,application/json表示請(qǐng)求體是json格式,application/xml表示是xml格式,而text/plain表示請(qǐng)求體是純文本。

那為什么將ContentType從application/x-www-form-urlencoded變成text/plain,報(bào)錯(cuò)的調(diào)用就能跑通了?

application/x-www-form-urlencoded有何不同?#

application/x-www-form-urlencoded是個(gè)歷史非常悠久的ContentType了,它通過key=value的形式來組織表單數(shù)據(jù),當(dāng)然key和value還需要做urlencode編碼。

而正是因?yàn)樗绱擞凭?,所以被采納在了web服務(wù)器的實(shí)現(xiàn)標(biāo)準(zhǔn)中,幾乎所有的web服務(wù)器,當(dāng)發(fā)現(xiàn)ContentType是application/x-www-form-urlencoded時(shí),會(huì)自動(dòng)按key=value&key2=value2的格式來解析請(qǐng)求體數(shù)據(jù),解析完成后,我們就可以通過request.getParameter()來獲取對(duì)應(yīng)key的值了。

比如Tomcat的實(shí)現(xiàn)在org.apache.catalina.connector.Request#parseParameters,如下:


解析key=value格式數(shù)據(jù)如下:

但是,這里有一個(gè)重要的細(xì)節(jié)!

當(dāng)ContentType是application/x-www-form-urlencoded時(shí),由于Tomcat提前將請(qǐng)求體的數(shù)據(jù)流讀了一遍,所以后面再通過request.getInputStream()就讀不到請(qǐng)求體數(shù)據(jù)了。

如下,從request.getInputStream()中獲取到的流,pos游標(biāo)已經(jīng)走到了lim結(jié)束位置了。

而將ContentType改為text/plain后,Tomcat不會(huì)解析請(qǐng)求體,所以就不會(huì)讀數(shù)據(jù)流,自然后面我們通過request.getInputStream()就又能讀到數(shù)據(jù)了,故又可以調(diào)通了!

解決問題#

解決這個(gè)問題很簡(jiǎn)單,如下:

  1. 讓調(diào)用方在請(qǐng)求體里加上data=,以符合application/x-www-form-urlencoded的key=value規(guī)范。

  2. 讓調(diào)用方將ContentType修改為text/plain,因?yàn)檎{(diào)用方的請(qǐng)求數(shù)據(jù)就是base64純文本而已,我們讓調(diào)用方選擇了這個(gè)方案。

如果調(diào)用方有很多,難以確定調(diào)用方的規(guī)范情況,那其實(shí)還有一種方案,通過request.getParameterMap()實(shí)現(xiàn),代碼有點(diǎn)hack(常規(guī)場(chǎng)景不推薦),如下:


這是因?yàn)?,?code>application/x-www-form-urlencoded中,key=value格式,value為空時(shí),可以傳key=,也可以省略掉等號(hào)傳key,所以我們?nèi)〉谝粋€(gè)key值就拿到了請(qǐng)求體數(shù)據(jù)。



由x-www-form-urlencoded引發(fā)的接口對(duì)接失敗的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
阿坝| 佛教| 长丰县| 蓝田县| 盘山县| 灵宝市| 清水县| 裕民县| 吐鲁番市| 蕉岭县| 观塘区| 兰州市| 漯河市| 方城县| 临桂县| 梨树县| 梧州市| 沙坪坝区| 海伦市| 禄丰县| 凤城市| 泰兴市| 南丹县| 衡阳县| 保康县| 长泰县| 黎平县| 肃宁县| 孟连| 克什克腾旗| 麻城市| 巴林右旗| 永靖县| 龙山县| 那曲县| 会宁县| 东宁县| 逊克县| 牙克石市| 仁怀市| 玉山县|