理解HTTP之Content-Type


0x01.About

查看 Restful API 報頭插件:Chrome插件REST Console,以及發(fā)送 Restful API 工具:Chrome插件POST Man

HTTP 1.1 規(guī)范中,HTTP 請求方式有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT。通常我們用的只有 GET、POST,然而對于 Restful API 規(guī)范來說,請求資源要用 PUT 方法,刪除資源要用 DELETE 方法。

例如發(fā)送個 DELETE 包:

http://example.com/my/resource?id=12345

那么通過 id 就能獲取到信息,這個包只有 header,并不存在 body,下面討論幾個包含body 的發(fā)包的 body 傳輸格式。

0x02.Content-Type

Content-Type 用于指定內(nèi)容類型,一般是指網(wǎng)頁中存在的 Content-TypeContent-Type
屬性指定請求和響應(yīng)的 HTTP 內(nèi)容類型。如果未指定 ContentType,默認(rèn)為 text/html

nginx 中有個配置文件 mime.types,主要是標(biāo)示 Content-Type 的文件格式。

下面是幾個常見的 Content-Type

  • text/html
  • text/plain
  • text/css
  • text/javascript
  • application/x-www-form-urlencoded
  • multipart/form-data
  • application/json
  • application/xml
  • ......

前面幾個都很好理解,都是 htmlcssjavascript 的文件類型,后面四個是 POST 的發(fā)包方式。

0x03.application/x-www-form-urlencoded

application/x-www-form-urlencoded 是常用的表單發(fā)包方式,普通的表單提交,或者 js 發(fā)包,默認(rèn)都是通過這種方式。

比如一個簡單地表單:

<form enctype="application/x-www-form-urlencoded" action="http://homeway.me/post.php" method="POST">
    <input type="text" name="name" value="homeway">
    <input type="text" name="key" value="nokey">
    <input type="submit" value="submit">
</form>

那么服務(wù)器收到的 raw header 會類似:

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,gl;q=0.2,de;q=0.2
Cache-Control:no-cache
Connection:keep-alive
Content-Length:17
Content-Type:application/x-www-form-urlencoded

那么服務(wù)器收到的 raw body 會是:name=homeway&key=nokey,在 php 中,通過$_POST 就可以獲得數(shù)組形式的數(shù)據(jù)。

0x04.multipart/form-data

multipart/form-data 用在發(fā)送文件的POST包。

這里假設(shè)我用 pythonrequest 發(fā)送一個文件給服務(wù)器:

data = {
    "key1": "123",
    "key2": "456",
}
files = {'file': open('index.py', 'rb')}
res = requests.post(url="http://localhost/upload", method="POST", data=data, files=files)
print res

通過工具,可以看到我發(fā)送的數(shù)據(jù)內(nèi)容如下:

POST http://www.homeway.me HTTP/1.1
Content-Type:multipart/form-data; boundary=------WebKitFormBoundaryOGkWPJsSaJCPWjZP

------WebKitFormBoundaryOGkWPJsSaJCPWjZP
Content-Disposition: form-data; name="key2"
456
------WebKitFormBoundaryOGkWPJsSaJCPWjZP
Content-Disposition: form-data; name="key1"
123
------WebKitFormBoundaryOGkWPJsSaJCPWjZP
Content-Disposition: form-data; name="file"; filename="index.py"

這里 Content-Type 告訴我們,發(fā)包是以 multipart/form-data 格式來傳輸,另外,還有 boundary 用于分割數(shù)據(jù)。

當(dāng)文件太長,HTTP 無法在一個包之內(nèi)發(fā)送完畢,就需要分割數(shù)據(jù),分割成一個一個 chunk 發(fā)送給服務(wù)端,
那么 -- 用于區(qū)分?jǐn)?shù)據(jù)快,而后面的數(shù)據(jù) ------WebKitFormBoundaryOGkWPJsSaJCPWjZP
就是標(biāo)示區(qū)分包作用。

0x05.text/xml

微信用的是這種數(shù)據(jù)格式發(fā)送請求的。

POST http://www.homeway.me HTTP/1.1 
Content-Type: text/xml

<?xml version="1.0"?>
<resource>
    <id>123</id>
    <params>
        <name>
            <value>homeway</value>
        </name>
        <age>
            <value>22</value>
        </age>
    </params>
</resource>

php$_POST 只能讀取 application/x-www-form-urlencoded 數(shù)據(jù),$_FILES 只能讀取 multipart/form-data 類型數(shù)據(jù),那么,要讀取 text/xml 格式的數(shù)據(jù),可以用:

file=fopen(‘php://input′,‘rb′);
data = fread(file,length);
fclose(file);

或者

$data = file_get_contents(‘php://input’);

0x06.application/json

通過 json 形式將數(shù)據(jù)發(fā)送給服務(wù)器,一開始,我嘗試通過 curl,給服務(wù)器發(fā)送application/json 格式包,然而我收到的數(shù)據(jù)如下:

————————–e1e1406176ee348a 
Content-Disposition: form-data; name=”nid” 2 ————————–e1e1406176ee348a 
Content-Disposition: form-data; name=”uuid” cf9dc994-a4e7-3ad6-bc54-41965b2a0dd7 ————————–e1e1406176ee348a 
Content-Disposition: form-data; name=”access_token” 956731586df41229dbfec08dd5d54eedb98d73d2 ————————–e1e1406176ee348a–

后來想想明白了,HTTP 通信中并不存在所謂的 json,而是將 string 轉(zhuǎn)成 json 罷了,也就是,application/json 可以將它理解為 text/plain,普通字符串。

之所以出現(xiàn)那么多亂七八糟的 ------- 應(yīng)該是 php 數(shù)組傳輸進(jìn)去,存在的轉(zhuǎn)換問題吧(我目前能想到的原因)。

本文出自 夏日小草,轉(zhuǎn)載請注明出處:http://homeway.me/2015/07/19/understand-http-about-content-type/

iOS 通過 POST 上傳圖片

iOS 上傳圖片以 multipart/form-data 進(jìn)行上傳。

  1. 使用 URLRequest
func uploadAvatar() {
    var request  = URLRequest(url: URL(string: "https://xxxxx")!)
    request.httpMethod = "POST"
    let boundary = "Boundary-\(UUID().uuidString)"
    let params = ["userId": "123", "token": "xxxxxxx"]
    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
    request.httpBody = createBody(parameters: params,
                            boundary: boundary,
                            data: UIImageJPEGRepresentation(chosenImage, 1.0)!,
                            mimeType: "image/jpg",
                            filename: "avatar.jpg")
}
func createBody(parameters: [String: String],
                boundary: String,
                data: Data,
                mimeType: String,
                filename: String) -> Data {
    let body = NSMutableData()
    
    let boundaryPrefix = "--\(boundary)\r\n"
    
    for (key, value) in parameters {
        body.appendString(boundaryPrefix)
        body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
        body.appendString("\(value)\r\n")
    }
    
    body.appendString(boundaryPrefix)
    body.appendString("Content-Disposition: form-data; name=\"file\"; filename=\"\(filename)\"\r\n")
    body.appendString("Content-Type: \(mimeType)\r\n\r\n")
    body.append(data)
    body.appendString("\r\n")
    body.appendString("--".appending(boundary.appending("--")))
    
    return body as Data
}
extension NSMutableData {
    func appendString(_ string: String) {
        let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
        append(data!)
    }
}
  1. 使用 Moya
/// 實際網(wǎng)絡(luò)請求對象
var task: Task {
    switch self {
    case .uploadAvatar(let model):
        guard let data = model.data else { return .request }
        let token = "xxxxxx".data(using: String.Encoding.utf8, allowLossyConversion: false)
        let userId = "123".data(using: String.Encoding.utf8, allowLossyConversion: false)
        let tokenData = MultipartFormData(provider: .data(token!), name: "token")
        let userIdData = MultipartFormData(provider: .data(userId!), name: "userId")
        let imgData = MultipartFormData(provider: .data(data), name: "file", fileName: "avatar.jpg", mimeType: "image/jpg")
        return .upload(.multipart([userIdData, tokenData, imgData]))
    default:
        return .request
    }
}

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,824評論 18 139
  • 整體Retrofit內(nèi)容如下: 1、Retrofit解析1之前哨站——理解RESTful2、Retrofit解析2...
    隔壁老李頭閱讀 15,126評論 4 39
  • # 一度蜜v3.0協(xié)議 --- # 交互協(xié)議 [TOC] ## 協(xié)議說明 ### 請求參數(shù) 下表列出了v3.0版協(xié)...
    c5e350bc5b40閱讀 662評論 0 0
  • HTTP全稱為HyperText Transfer Protocol,從名字不難看出這是一種基于文本的網(wǎng)絡(luò)協(xié)議,對...
    MrPeak閱讀 1,474評論 3 21
  • 取前3個字符 取后3個字符 遍歷字符串 插入字符串 替換字符串 字符串切片,獲取interesting 字符串切片...
    Roct閱讀 1,319評論 0 2