Alamofire之Response

在前面的幾篇文章中已經介紹了Alamofire的request。本篇咱們一塊兒來了解一下response。swift

1、reponse函數

說到response不少小夥伴都會和序列化聯繫起來,那麼Alamofire的response的做用究竟是什麼呢?數組

咱們來看個栗子🌰:網絡

let urlBD = "https://www.baidu.com"

Alamofire.request(urlBD, method: .get, parameters: ["user": "bo"])
    .response { (response) in
        print(response)
}
複製代碼

運行結果會打印不少內容,因篇幅緣由就不貼圖了,就留給小夥伴自行玩耍吧。session

這些 response 中的內容是如何來的呢?咱們一塊兒來看看源碼。 閉包

從源碼能夠看出,response 函數並無作序列化的處理。而是使用 Requestself.requestself.responseself.delegateself.timeline 屬性封裝成一個新的對象 DefaultDataResponse返回給 completionHandler 閉包。app

由於 response 函數是 DataRequest 的擴展。因此 self.requestself.response 比較容易理解。那麼 self.delegate.data 是什麼呢?咱們點擊查看源碼: dom

能夠看到,若是 dataStream 閉包爲空,則返回 mutableData 。因此咱們再繼續查看 mutableData 是何時賦值的。 異步

咱們找到在 func urlSession(session:dataTask:didReceive:) 回調中,返回的數據 data 會被添加到 mutableData 中。函數

爲何須要 mutableData 呢?由於請求的數據多是分段返回的,因此須要一箇中間變量來存儲每次返回的數據。工具

self.delegate.data 相似, self.delegate.error 也是在回調函數 didCompleteWithError 中判斷,若是有錯誤,則給 error 賦值。

至於 self.timeline,咱們在上一篇 Alamofire之request補充 已有介紹,這裏再也不贅述,想了解的小夥伴請移步。

綜上,能夠總結出,其實 response 函數並無作其餘處理,僅作爲保存數據和返回數據的載體,其整合了request請求的各個返回結果,做爲一個總體返回給用戶,供用戶隨時取用。

2、序列化

在查看 response 函數源碼的時候,若是往下翻小夥伴可能會發現另外一個 reponse 函數。

這個 response 函數和上一個相比,多了一個 responseSerializer 參數,這個參數就是序列化器。

那麼這個 response 函數該如何使用呢?咱們先來分析 responseSerializer 參數是個什麼類型。

responseSerializer 的參數是一個泛型。泛型須要實現一個協議: DataResponseSerializerProtocol

同時咱們在其下方發現一個已經實現該協議的類型 DataResponseSerializer

因此咱們可使用這個類做爲序列化器。

let urlBD = "https://www.baidu.com"

Alamofire.request(urlBD, method: .get, parameters: ["user": "bo"])
    .response { (response) in
        print(response)
    }
    .response(responseSerializer: DataResponseSerializer<String>.init(serializeResponse: { (request, response, data, error) -> Result<String> in
        
        if error == nil {
            
            return .success("網絡請求成功")
        } else {
            return .failure(NSError(domain: "網絡請求失敗", code: 10080, userInfo: nil))
        }
    })) { (reponse) in
        
        print("序列化結果: \(reponse)")
}
複製代碼

DataResponseSerializer 初始化須要傳入一個閉包,且閉包的返回值爲 Result 類型。

從運行結果會發現,在打印出未序列化的 reposne 以後,會再打印出序列化後的結果。

以上就是response序列化器的分析,包括 Alamofire提供的 responseJSON 函數,其內部僅是對 JSON序列化器作了一次封裝而已。

3、多表單上傳

先舉個栗子🌰:

let url = "http://www.baidu.com/"

Alamofire.upload(multipartFormData: { (formData) in
    
    formData.append("BO".data(using: .utf8)!, withName: "name")
    formData.append("122345".data(using: .utf8)!, withName: "pwd")
    
}, to: url) { (result) in
    
    print(result)
}
複製代碼

上面👆這段代碼就是多表單上傳的例子。而後使用抓包工具 Charles 抓包。

就能夠看到如上所示的結果。

其實在進行多表單上傳時,須要將表單數據按照必定的格式(如上圖)封裝到httpBody中。具體格式請小夥伴自行google。

那麼Alamofire是如何封裝的呢?咱們一路查看源碼,很容易就能找到這裏:

首先由於在讀取數據時多是一個耗時操做,因此

  • 在異步線程中初始化一個 MultipartFormData 對象。
  • 再調用 multipartFormData 閉包,拼接表單數據。
  • 在發起網絡請求以前,須要先設置 Content-Type,代表這是一個表單數據,以及 boundary 邊界信息。
  • 再調用 formData.encode() 格式化數據。

一、添加表單

咱們在向表單中添加數據時,會調用 append 函數添加數據。

append 函數中,參數 name 會被先傳入 contentHeaders 函數中進行處理。

通過處理後會成爲咱們所須要的格式。

而後再將格式化後的 headersdata 封裝成 BodyPart 對象。

最後添加到 bodyParts 數組中保存起來。

二、encode表單

須要格式化表單時,會調用 encode 函數。

encode 函數會將 bodyParts 中的首尾兩個元素分別設置 hasInitialBoundary = truehasFinalBoundary = true,標識出其是首尾元素。

再遍歷 bodyParts 數組中的元素,對每個元素分別進行 encode

一、格式化邊界

首先經過 hasInitialBoundary 判斷是首元素仍是中間元素。

  • 若是是首元素,則調用 initialBoundaryData() 函數。
  • 若是是中間元素,則調用 encapsulatedBoundaryData() 函數。
  • 若是是尾元素,則調用 finalBoundaryData() 函數。

這三個函數調用的都是 boundaryData 函數,僅參數不一樣。

因此,最後生成的邊界格式會有所不一樣。如: 首元素:--alamofire.boundary.xxx 中間元素:--alamofire.boundary.xxx 尾元素:--alamofire.boundary.xxx--

二、格式化name

對每一個元素調用 encodeHeaders 函數。

最後生成格式以下: Content-Disposition: form-data; name="name"

三、格式化data

對每一個元素調用 encodeBodyStream 函數。

由於表單數據多是一個很大的文件,若是要將文件整個讀取到內存中,將會消耗掉很大的內存空間,形成資源緊張。因此使用 數據流stream 的方式讀取數據。生成格式如:BO。

最後將生成的全部數據拼接爲一個data數據,完成表單數據的封裝。

以上,則是多表單上傳中,表單數據的格式化解析。

相關文章
相關標籤/搜索