http content-type那些你不知道事

1 概述

我之前一直做android的漆撞,后來也做過ios,也寫過REACT-NATIVE。
以前一直沒有什么寫文章習慣于宙,但發(fā)現(xiàn)浮驳,好多知識容易忘記,還是決定記錄下來吧捞魁,不然很多知識找起來麻煩抹恳。也希望能跟廣大同行們交流經驗,互相學習.如果有寫的不好署驻,有錯誤的地方奋献,歡迎指正哈健霹!說了一堆廢話,下面進入正題.

2 POST請求的常用格式

HTTP/1.1協(xié)議規(guī)定的HTTP請求方法有OPTIONS瓶蚂、GET糖埋、HEAD、POST窃这、PUT瞳别、DELETE、TRACE杭攻、CONNECT 這幾種祟敛。其中POST一般用來向服務端提交數據,接下來要討論POST提交數據的幾種方式兆解。協(xié)議規(guī)定POST提交的數據必須放在消息主體中馆铁,但協(xié)議并沒有規(guī)定數據必須使用什么編碼方式。
實際上锅睛,開發(fā)者完全可以自己決定消息主體的格式埠巨,只要最后發(fā)送的 HTTP 請求滿足上面的格式就可以。

但是现拒,數據發(fā)送出去辣垒,還要服務端解析成功才有意義。一般服務端語言如java印蔬、php勋桶、python等,以及它們的framework侥猬,都內置了自動解析常見數據格式的功能例驹。服務端通常是根據請求頭(headers)中的Content-Type字段來獲知請求中的消息主體是用何種方式編碼,再對主體進行解析陵究。所以說到POST提交數據方案,包含了Content-Type和消息主體編碼方式兩部分奥帘。

2.1 application/x-www-form-urlencoded格式的POST請求

這應該是最常見的 POST 提交數據的方式了铜邮。瀏覽器的原生表單,如果不設置enctype屬性寨蹋,那么最終就會以application/x-www-form-urlencoded方式提交數據松蒜。Content-Type被指定為application/x-www-form-urlencoded,提交的數據按照 key1=val1&key2=val2的方式進行編碼已旧,key和val都進行了URL轉碼秸苗。

下面這個請求是簡書進入一篇文章頁面的時候,會自動往服務器POST一個請求运褪,估計是統(tǒng)計文章被閱讀的次數等功能惊楼。具體看下面:

//發(fā)送的請求玖瘸,刪除了cookie相關的部分
POST /notes/e15592ce40ae/mark_viewed.json HTTP/1.1
Host: www.reibang.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
X-CSRF-Token: vJvptva4Tqou/V3dd3nFCrcvRsb78FReHuIYZke5PVAnfR/tIAAMCfuaB2Z2/gaEohIZAsiEksUYyPqzg3DpSA==
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://www.reibang.com/p/e15592ce40ae
Content-Length: 98
Connection: keep-alive
Cache-Control: max-age=0
//請求體

uuid=4e3abc0f-1824-4a5d-982f-7d9dee92d9cd&referrer=http%3A%2F%2Fwww.reibang.com%2Fu%2Fad726ba6935d
用AFHTTPSessionManager實現(xiàn)上面這個application/x-www-form-urlencoded請求。

//以下是實際代碼
    AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSDictionary *params = @{
                             @"uuid":@"4e3abc0f-1824-4a5d-982f-7d9dee92d9cd",
                             @"referrer":@"http://www.reibang.com/p/e15592ce40ae"
                             };
    NSURLSessionDataTask *task = [manager POST:@"http://www.reibang.com//notes/e15592ce40ae/mark_viewed.json" parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
        NSLog(@"進度更新");
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"返回數據:%@",responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"返回錯誤:%@",error);
    }];
    [task resume];

2.2 multipart/form-data格式的POST請求

Multipart/form-data的基礎方法是POST , 也就是說是由POST方法來組合實現(xiàn)的. Multipart/form-data與POST方法的不同之處在于請求頭和請求體. Multipart/form-data的請求頭必須包含一個特殊的頭信息 : Content-Type , 且其值也必須規(guī)定為multipart/form-data , 同時還需要規(guī)定一個內容分割符用于分割請求體中的多個POST的內容 , 如文件內容和文本內容自然需要分割開來 , 不然接收方就無法正常解析和還原這個文件了. Multipart/form-data的請求體也是一個字符串 , 不過和post的請求體不同的是它的構造方式 , post是簡單的name=value值連接 , 而Multipart/form-data則是添加了分隔符等內容的構造體.

請求的頭部信息如下:

//其中xxxxx是我自定義的分隔符檀咙,每個人都可以選擇自己的分隔符
Content-Type: multipart/form-data; boundary=xxxxx
下面我們來看一下一個我的Multipart/form-data請求體:

POST /uploadFile HTTP/1.1
Host: 這里是url,就不暴露了_
Content-Type: multipart/form-data; boundary=xxxxx
Connection: keep-alive
Accept: /
User-Agent: AFNetWorking3.X%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/1 CFNetwork/808.2.16 Darwin/15.6.0
Content-Length: 32175
Accept-Language: en-us
Accept-Encoding: gzip, deflate

--xxxxx
Content-Disposition: form-data;name="file"

img.jpeg
--xxxxx
Content-Disposition: form-data;name="businessType"

CC_USER_CENTER
--xxxxx
Content-Disposition: form-data;name="fileType"

image
--xxxxx
Content-Disposition:form-data;name="file";filename="img1.jpeg"
Content-Type:image/png

這里是圖片數據,太長了.我就刪了

--xxxxx--
這個請求有三個參數file,businessType,fileType雅倒。比如file參數和他的值就通過如下格式傳輸:

--xxxxx
Content-Disposition: form-data;name="file"

img.jpeg
上面這種就是一個參數與之對應的值。協(xié)議規(guī)定的就是這個格式弧可,沒有為什么蔑匣。我們可以看看圖片數據部分:

--xxxxx
Content-Disposition:form-data;name="file";filename="img1.jpeg"
Content-Type:image/png

這里是圖片數據,太長了.我就刪了

--xxxxx--
其中name="參數名" filename="文件名" 其中參數名這個要和接收方那邊相對應 正常開發(fā)中可以去問服務器那邊 , 文件名是說在服務器端保存成文件的名字 , 這個參數然并卵 , 因為一般服務端會按照他們自己的要求去處理文件的存儲.

下一行是指定類型 , 我這里示例中寫的是PNG圖片類型 , 這個可以根據你的實際需求的寫。如果我們要上傳多分圖片或者文件棕诵,則只需要按照指定格式就可以了裁良,比如下面就是上傳兩張圖片的請求:

POST /uploadFile HTTP/1.1
Host: 這里是url,就不暴露了_
Content-Type: multipart/form-data; boundary=xxxxx
Connection: keep-alive
Accept: /
User-Agent: AFNetWorking3.X%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/1 CFNetwork/808.2.16 Darwin/15.6.0
Content-Length: 32175
Accept-Language: en-us
Accept-Encoding: gzip, deflate

--xxxxx
Content-Disposition: form-data;name="file"

img.jpeg
--xxxxx
Content-Disposition: form-data;name="businessType"

CC_USER_CENTER
--xxxxx
Content-Disposition: form-data;name="fileType"

image
--xxxxx
Content-Disposition:form-data;name="file";filename="img1.jpeg"
Content-Type:image/png

這里是圖片1數據,太長了.我就刪了
--xxxxx
Content-Disposition:form-data;name="file";filename="img2.jpeg"
Content-Type:image/png

這里是圖片1數據,太長了.我就刪了
--xxxxx--
下面是我Demo中一個multipart/form-data請求的實現(xiàn)代碼,分別用NSRULDataTask和AFHTTPSessionManager實現(xiàn),我們可以發(fā)現(xiàn)用第二種方法簡便了很多,因為AFN已經幫我們做好了拼接工作:

//方法一
- (IBAction)updatePic:(id)sender {
    //請求頭參數
    NSDictionary *dic = @{
                          @"businessType":@"CC_USER_CENTER",
                          @"fileType":@"image",
                          @"file":@"img.jpeg"
                          };
    //請求體圖片數據
    NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed:@"1.png"]);
    //創(chuàng)建request
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:url]];
    //post方法
    [request setHTTPMethod:@"POST"];
    // 設置請求頭格式為Content-Type:multipart/form-data; boundary=xxxxx
    //[request setValue:@"multipart/form-data; boundary=xxxxx" forHTTPHeaderField:@"Content-Type"];
    AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSURLSessionDataTask *task = [manager POST:url parameters:dic constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        //請求體里面的參數
        NSDictionary *bodyDic = @{
                                  @"Content-Disposition":@"form-data;name=\"file\";filename=\"img.jpeg\"",
                                  @"Content-Type":@"image/png",
                                  };
        [formData appendPartWithHeaders:bodyDic body:imageData];
    } progress:^(NSProgress * _Nonnull uploadProgress) {
        NSLog(@"下載進度");
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"下載成功:%@",responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"下載失敗%@",error);
    }];
    [task resume];
}
//方法二
- (IBAction)multipartformPost2:(id)sender {
    //參數
    NSDictionary *dic = @{
                          @"businessType":@"CC_USER_CENTER",
                          @"fileType":@"image",
                          @"file":@"img.jpeg"
                          };
    NSString *boundaryString = @"xxxxx";
    NSMutableString *str = [NSMutableString string];
    [dic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        [str appendFormat:@"--%@\r\n",boundaryString];
        [str appendFormat:@"%@name=\"%@\"\r\n\r\n",@"Content-Disposition: form-data;",key];
        [str appendFormat:@"%@\r\n",obj];
    }];

     NSMutableData *requestMutableData=[NSMutableData data];

    [str appendFormat:@"--%@\r\n",boundaryString];
    [str appendFormat:@"%@:%@",@"Content-Disposition",@"form-data;"];
    [str appendFormat:@"%@=\"%@\";",@"name",@"file"];
    [str appendFormat:@"%@=\"%@\"\r\n",@"filename",@"img1.jpeg"];
    [str appendFormat:@"%@:%@\r\n\r\n",@"Content-Type",@"image/png"];
    //轉換成為二進制數據
    [requestMutableData appendData:[str dataUsingEncoding:NSUTF8StringEncoding]];
    NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed:@"1.png"]);
    //文件數據部分
    [requestMutableData appendData:imageData];
    //添加結尾boundary
    [requestMutableData appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundaryString] dataUsingEncoding:NSUTF8StringEncoding]];
    //創(chuàng)建一個請求對象
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:url]];
    //post方法
    [request setHTTPMethod:@"POST"];
    // 設置請求頭格式為Content-Type:multipart/form-data; boundary=xxxxx
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundaryString] forHTTPHeaderField:@"Content-Type"];
    //session
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSURLSessionDataTask *task = [session uploadTaskWithRequest:request fromData:requestMutableData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSString *result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@",result);
    }];
    [task resume];
}

Multipart/form-data格式的POST請求總結:

文件類型參數中name="參數名"一定要和服務端對應, 開發(fā)的時候 , 可以問服務端人員校套,我這里是file价脾。
上傳文件的數據部分使用二進制數據(NSData)拼接。
上邊界部分和下邊界部分的字符串 , 最后都要轉換成二進制數據(NSData) , 和文件部分的二進制數據拼接在一起 , 作為請求體發(fā)送給服務器搔确。
每一行末尾需要有一定的`\r\n·彼棍。

2.3 application/json格式的POST請求

接下來我將常使用NSURLSessionDataTask做一個application/json的POST請求。并且請求體數據我存儲在一個test.txt文件中膳算,從文件中讀取出來然后上傳座硕。

//test.txt文件內容
{"name":"尋歡","phone":"187***"}
通過抓包軟件我的請求如下,和其他POST請求原理一樣涕蜂,只是拼接請求體的方式不一樣华匾,并且更具不同格式的請求體,設置不同的Content-Type:

POST /posts HTTP/1.1
Host: jsonplaceholder.typicode.com
Content-Type: application/json
Connection: keep-alive
Accept: application/json
User-Agent: AFNetWorking3.X%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/1 CFNetwork/808.2.16 Darwin/15.6.0
Content-Length: 31
Accept-Language: en-us
Accept-Encoding: gzip, deflate

{"name":"尋歡","phone":"187***"}
下面是我Demo的具體實現(xiàn)

- (IBAction)applicationjsonPOST2:(id)sender {
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://jsonplaceholder.typicode.com/posts"]];
    //指請求體的類型机隙。由于我們test.txt里面的文件是json格式的字符串蜘拉。所以我這里指定為`application/json`
    [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [request setHTTPMethod:@"POST"];
    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
    [request setTimeoutInterval:20];
    NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"txt"];
    NSURL *url = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    //使用Block來處理返回數據
    NSURLSessionDataTask *task = [session uploadTaskWithRequest:request fromFile:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSString *result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@",result);
    }];
    [task resume];
}

3 總結

當然conent-type 還有別的application/xml,application/javascript,text/plain 有鹿,這個請求頭的屬性旭旭,至始至終都提供了讓我們自定義請求體的格式,當然需要服務器配合.不過我們這些做前段的通常都是配合服務器的哈哈葱跋。不過持寄,現(xiàn)在的框架基本都為我們做了這些請求體格式的拼接工作.

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市娱俺,隨后出現(xiàn)的幾起案子稍味,更是在濱河造成了極大的恐慌,老刑警劉巖荠卷,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件模庐,死亡現(xiàn)場離奇詭異,居然都是意外死亡油宜,警方通過查閱死者的電腦和手機掂碱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門怜姿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人顶吮,你說我怎么就攤上這事社牲。” “怎么了悴了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵搏恤,是天一觀的道長。 經常有香客問我湃交,道長熟空,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任搞莺,我火速辦了婚禮息罗,結果婚禮上,老公的妹妹穿的比我還像新娘才沧。我一直安慰自己迈喉,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布温圆。 她就那樣靜靜地躺著挨摸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岁歉。 梳的紋絲不亂的頭發(fā)上得运,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音锅移,去河邊找鬼熔掺。 笑死,一個胖子當著我的面吹牛非剃,可吹牛的內容都是我干的置逻。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼备绽,長吁一口氣:“原來是場噩夢啊……” “哼券坞!你這毒婦竟也來了?” 一聲冷哼從身側響起疯坤,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤报慕,失蹤者是張志新(化名)和其女友劉穎深浮,沒想到半個月后压怠,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡飞苇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年菌瘫,在試婚紗的時候發(fā)現(xiàn)自己被綠了蜗顽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡雨让,死狀恐怖雇盖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情栖忠,我是刑警寧澤崔挖,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站庵寞,受9級特大地震影響狸相,放射性物質發(fā)生泄漏。R本人自食惡果不足惜捐川,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一脓鹃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧古沥,春花似錦瘸右、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纯衍,卻和暖如春栋齿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背襟诸。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工瓦堵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人歌亲。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓菇用,卻偏偏與公主長得像,于是被迫代替她去往敵國和親陷揪。 傳聞我的和親對象是個殘疾皇子惋鸥,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)悍缠,斷路器卦绣,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • 0x01.About 查看 Restful API 報頭插件:Chrome插件REST Console,以及發(fā)送 ...
    牽線小丑閱讀 4,811評論 0 15
  • Python 面向對象Python從設計之初就已經是一門面向對象的語言飞蚓,正因為如此滤港,在Python中創(chuàng)建一個類和對...
    順毛閱讀 4,211評論 4 16
  • iOS開發(fā)系列--網絡開發(fā) 概覽 大部分應用程序都或多或少會牽扯到網絡開發(fā),例如說新浪微博趴拧、微信等溅漾,這些應用本身可...
    lichengjin閱讀 3,648評論 2 7
  • Address:https://www.zybuluo.com/XiangZhou/note/208532 Exp...
    天蠍蒗漫閱讀 11,289評論 2 55