理解HTTP之Content-Type


0x01.About

查看 Restful API 報(bào)頭插件: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-Type,Content-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
  • ......

前面幾個都很好理解,都是 html、cssjavascript 的文件類型,后面四個是 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
/// 實(shí)際網(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容