最近遇到的問題記錄:UrlEncode、UrlDecode


本文閱讀前了解知識(shí):什么時(shí)候需要使用UrlEncode和UrlDecode函數(shù)
作者使用谷歌瀏覽器,通過按下F12對(duì)第三方網(wǎng)站http協(xié)議的接口抓包進(jìn)行分析操作。
場(chǎng)景
運(yùn)維小哥哥偶爾使用某某外包公司的網(wǎng)站系統(tǒng),做設(shè)備錄入工作,流程簡(jiǎn)單:

錄入設(shè)備基本信息,有7、8個(gè)字段需要輸入,然后點(diǎn)擊保存按鈕; 基本信息保存成功,進(jìn)入設(shè)備類型選擇操作,然后點(diǎn)擊生成設(shè)備標(biāo)識(shí)按鈕; 設(shè)備標(biāo)識(shí)生成成功,錄入設(shè)備關(guān)聯(lián)的模塊信息,簡(jiǎn)單設(shè)備只需要錄入2條模塊,復(fù)雜的設(shè)備有6條模塊,每個(gè)模塊有3、4個(gè)字段需要輸入,最后點(diǎn)擊保存。
一條設(shè)備錄入成功,單身多年的手速可能也花不了幾分鐘,其實(shí)這也沒啥。
突然領(lǐng)導(dǎo)說有1000個(gè)設(shè)備需要搞?運(yùn)維小哥哥哭了??,這時(shí)就該開發(fā)人員上場(chǎng)了:
運(yùn)維準(zhǔn)備一個(gè)Excel模板,輸入需要錄入的1000個(gè)設(shè)備基本信息、設(shè)備類型信息,這個(gè)工作量不大,就半天吧,最多一天工作量; 開發(fā)做個(gè)C/S客戶端小工具,程序中按業(yè)務(wù)要求配置模塊錄入規(guī)則; 程序執(zhí)行過程中錄入一個(gè)設(shè)備就把生成的設(shè)備標(biāo)識(shí)與設(shè)備關(guān)聯(lián); 全部錄入完成,提供一個(gè)Excel導(dǎo)出,可將設(shè)備基本信息、生成的設(shè)備標(biāo)識(shí)全部關(guān)聯(lián)導(dǎo)出,工作完成。
經(jīng)過幾天的開發(fā)工作,開發(fā)哥哥將精心打磨的小工具交給運(yùn)維小哥,運(yùn)維小哥哥使用后投來了贊許的目光...
問題
前面鋪墊的話有點(diǎn)啰嗦了,開發(fā)這個(gè)小工具時(shí),開發(fā)小哥遇到一個(gè)問題:

這是某個(gè)接口的信息,Content-Type 是 application/x-www-form-urlencoded,下面參數(shù)使用的Form Data,即參數(shù)使用了UrlEncode,比如未編碼前的一個(gè)參數(shù):
"Content":"{"AP_Name":"HK_7889","IP":"192.168.0.1"}"
編碼后(可以使用這個(gè)在線URL編碼解碼工具驗(yàn)證):
"Content":"%7B%22AP_Name%22%3A%22HK_7889%22%2C%22IP%22%3A%2292.168.0.1%22%7D"
使用Postman測(cè)試時(shí),未對(duì)參數(shù)使用UrlEncode,接口測(cè)試成功,開發(fā)這個(gè)小工具時(shí),有3個(gè)接口都是類似的,未進(jìn)行UrlEncode操作:
var?client?=?new?RestClient("http://admin.lqclass.com/api/device");
client.Timeout?=?-1;
var?request?=?new?RestRequest(Method.POST);
request.AddHeader("Content-Type",?"application/x-www-form-urlencoded");
request.AddParameter("Content",?"{\"AP_Name\":\"HK_7889\",\"IP\":\"92.168.0.1\"}");
IRestResponse?response?=?client.Execute(request);
Console.WriteLine(response.Content);
但遇到稍微復(fù)雜一點(diǎn)的接口,比如截圖中的參數(shù)為:
"Content":"{"AP_Name":"HK_7889","IP":"192.168.0.1","Module":[{"M_Name":"cameri0","Desc":"cameri0","AP_PUID":"54632325461320320"},{"M_Name":"cameri1","Desc":"cameri1","AP_PUID":"54636325461320320"},{"M_Name":"cameri2","Desc":"cameri2","AP_PUID":"54632325421320320"}]}"
Content值格式化看得清楚一點(diǎn),Module是設(shè)備關(guān)聯(lián)的模塊信息:
{
??"AP_Name":?"HK_7889",
??"IP":?"192.168.0.1",
??"Module":?[
????{
??????"M_Name":?"cameri0",
??????"Desc":?"cameri0",
??????"AP_PUID":?"54632325461320320"
????},
????{
??????"M_Name":?"cameri1",
??????"Desc":?"cameri1",
??????"AP_PUID":?"54636325461320320"
????},
????{
??????"M_Name":?"cameri2",
??????"Desc":?"cameri2",
??????"AP_PUID":?"54632325421320320"
????}
??]
}
實(shí)際UrlEncode后的參數(shù)為:
"Content":"%7B%22AP_Name%22%3A%22HK_7889%22%2C%22IP%22%3A%22192.168.0.1%22%2C%22Module%22%3A%22%255B%257B%2522M_Name%2522%253A%2522cameri0%2522%252C%2522Desc%2522%253A%2522cameri0%2522%252C%2522AP_PUID%2522%253A%252254632325461320320%2522%257D%252C%257B%2522M_Name%2522%253A%2522cameri1%2522%252C%2522Desc%2522%253A%2522cameri1%2522%252C%2522AP_PUID%2522%253A%252254636325461320320%2522%257D%252C%257B%2522M_Name%2522%253A%2522cameri2%2522%252C%2522Desc%2522%253A%2522cameri2%2522%252C%2522AP_PUID%2522%253A%252254632325421320320%2522%257D%255D%22%7D"
本來一般接口,如上面成功執(zhí)行的C#代碼那般直接未UrlEncode調(diào)用是沒問題的。
但這個(gè)接口調(diào)用,服務(wù)器返回錯(cuò)誤信息:“xxx解析失敗”,調(diào)用代碼如下:
var?client?=?new?RestClient("http://admin.lqclass.com/api/device");
client.Timeout?=?-1;
var?request?=?new?RestRequest(Method.POST);
request.AddHeader("Content-Type",?"application/x-www-form-urlencoded");
request.AddParameter("Content",?"{\"AP_Name\":\"HK_7889\",\"IP\":\"192.168.0.1\",\"Module\":[{\"M_Name\":\"cameri0\",\"Desc\":\"cameri0\",\"AP_PUID\":\"54632325461320320\"},{\"M_Name\":\"cameri1\",\"Desc\":\"cameri1\",\"AP_PUID\":\"54636325461320320\"},{\"M_Name\":\"cameri2\",\"Desc\":\"cameri2\",\"AP_PUID\":\"54632325421320320\"}]}");
IRestResponse?response?=?client.Execute(request);
Console.WriteLine(response.Content);
兩處調(diào)用代碼哪里不同?只是Content值不一樣,最后懷疑是不是需要手動(dòng)進(jìn)行UrlEncode?又不是url參數(shù),為啥需要編碼呢?不管啦,先編碼了再說。
問題解決
參數(shù)編碼后,調(diào)用:
var?client?=?new?RestClient("http://admin.lqclass.com/api/device");
client.Timeout?=?-1;
var?request?=?new?RestRequest(Method.POST);
request.AddHeader("Content-Type",?"application/x-www-form-urlencoded");
request.AddParameter("Content",?"%7B%22AP_Name%22%3A%22HK_7889%22%2C%22IP%22%3A%22192.168.0.1%22%2C%22Module%22%3A%22%255B%257B%2522M_Name%2522%253A%2522cameri0%2522%252C%2522Desc%2522%253A%2522cameri0%2522%252C%2522AP_PUID%2522%253A%252254632325461320320%2522%257D%252C%257B%2522M_Name%2522%253A%2522cameri1%2522%252C%2522Desc%2522%253A%2522cameri1%2522%252C%2522AP_PUID%2522%253A%252254636325461320320%2522%257D%252C%257B%2522M_Name%2522%253A%2522cameri2%2522%252C%2522Desc%2522%253A%2522cameri2%2522%252C%2522AP_PUID%2522%253A%252254632325421320320%2522%257D%255D%22%7D");
IRestResponse?response?=?client.Execute(request);
Console.WriteLine(response.Content);
哈哈,成功了,這里簡(jiǎn)單猜測(cè)下:別人的服務(wù)對(duì)接收的參數(shù)可能做了UrlDecode操作。
其實(shí)中間還做了一個(gè)參數(shù)的UrlEncode操作,即下面的Module參數(shù)值:
"Content":{"AP_Name":"HK_7889","IP":"192.168.0.1","Module":[{"M_Name":"cameri0","Desc":"cameri0","AP_PUID":"54632325461320320"},{"M_Name":"cameri1","Desc":"cameri1","AP_PUID":"54636325461320320"},{"M_Name":"cameri2","Desc":"cameri2","AP_PUID":"54632325421320320"}]}
第一次UrlEncode,即先對(duì)Module的值進(jìn)行UrlEncode:
"Content":{"AP_Name":"HK_7889","IP":"192.168.0.1","Module":%5B%7B%22M_Name%22%3A%22cameri0%22%2C%22Desc%22%3A%22cameri0%22%2C%22AP_PUID%22%3A%2254632325461320320%22%7D%2C%7B%22M_Name%22%3A%22cameri1%22%2C%22Desc%22%3A%22cameri1%22%2C%22AP_PUID%22%3A%2254636325461320320%22%7D%2C%7B%22M_Name%22%3A%22cameri2%22%2C%22Desc%22%3A%22cameri2%22%2C%22AP_PUID%22%3A%2254632325421320320%22%7D%5D}
第二次UrlEncode即是上面成功的參數(shù)方式了,對(duì)整個(gè)Content的值進(jìn)行UrlEncode,看上面成功的參數(shù),不重復(fù)貼了。
最后總結(jié)
抓別人數(shù)據(jù)包時(shí),不要憑印象、已有知識(shí)判定該怎么怎么做,比如前面的參數(shù),不使用UrlEncode時(shí),調(diào)用成功了,其他包我是否也沿用相同的方式使用就正確呢?搞不定時(shí),多嘗試猜測(cè)的方法。
總結(jié):“管他的,干就是了”。
本文使用的UrlEncode C# 代碼:
public?static?string?UrlEncode(string?str)
{
????StringBuilder?sb?=?new?StringBuilder();
????byte[]?byStr?=?System.Text.Encoding.UTF8.GetBytes(str);?//默認(rèn)是System.Text.Encoding.Default.GetBytes(str)
????for?(int?i?=?0;?i?????{
????????sb.Append(@"%"?+?Convert.ToString(byStr[i],?16));
????}
????return?(sb.ToString());
}
文末分享
后臺(tái)回復(fù)數(shù)字【00】:獲取DotNet、Java、C++、前端等技術(shù)資料 添加微信群:添加號(hào)主微信號(hào)【dotnet9】,備注【入群】 添加QQ群:群號(hào)【771992300】,備注【Dotnet9】
?時(shí)間如流水,只能流去不流回。
公眾號(hào):Dotnet9 號(hào)主微信號(hào):dotnet9 作者及編輯:沙漠之盡頭的狼 原文鏈接:點(diǎn)擊閱讀 日期:2021-01-09 微信公眾號(hào):Dotnet9
