原創(chuàng):知識(shí)點(diǎn)總結(jié)性文章
創(chuàng)作不易,請(qǐng)珍惜蛛壳,之后會(huì)持續(xù)更新杏瞻,不斷完善
個(gè)人比較喜歡做筆記和寫(xiě)總結(jié),畢竟好記性不如爛筆頭哈哈衙荐,這些文章記錄了我的IOS成長(zhǎng)歷程捞挥,希望能與大家一起進(jìn)步
溫馨提示:由于簡(jiǎn)書(shū)不支持目錄跳轉(zhuǎn),大家可通過(guò)command + F 輸入目錄標(biāo)題后迅速尋找到你所需要的內(nèi)容
目錄
- 一忧吟、系統(tǒng)提供的原生框架URLSession的使用
- 1砌函、請(qǐng)求網(wǎng)絡(luò)的流程
- 2、屬性
- 3溜族、HTTP
- 二讹俊、初涉Alamofire
- 1、發(fā)起請(qǐng)求
- 2煌抒、HTTP Methods
- 3仍劈、請(qǐng)求參數(shù)和參數(shù)編碼器
- 4、HTTP Headers
- 5摧玫、響應(yīng)驗(yàn)證
- 6耳奕、響應(yīng)處理
- 7绑青、身份驗(yàn)證
- 8诬像、下載文件
- 9屋群、上傳數(shù)據(jù)到服務(wù)器
- 10、網(wǎng)絡(luò)可達(dá)性
- 三坏挠、玩轉(zhuǎn)Alamofire
- 1芍躏、Session
- 2、Request
- 3降狠、Security
- Demo
- 參考文獻(xiàn)
一对竣、系統(tǒng)提供的原生框架URLSession的使用
1、請(qǐng)求網(wǎng)絡(luò)的流程
a榜配、請(qǐng)求網(wǎng)絡(luò)的基本方式
-
URLSession.shared
提供了一個(gè)共享的單例會(huì)話對(duì)象否纬,它為創(chuàng)建任務(wù)提供了一個(gè)默認(rèn)行為。使用共享會(huì)話僅用幾行代碼就可以將URL
的內(nèi)容獲取到蛋褥。 -
dataTask
創(chuàng)建一個(gè)網(wǎng)絡(luò)會(huì)話數(shù)據(jù)任務(wù)临燃。 - 網(wǎng)絡(luò)任務(wù)默認(rèn)是掛起的,調(diào)用
resume
開(kāi)始進(jìn)行連接請(qǐng)求網(wǎng)絡(luò) - 請(qǐng)求成功或者失敗都會(huì)返回結(jié)果閉包烙心,其實(shí)閉包只是一層封裝膜廊,真正來(lái)的是
URLSession
的代理 - 在下面的過(guò)程中,我們省略了一個(gè)重要的東西:
URLSessionConfiguration
let url = URL(string: "https://www.baidu.com")!
URLSession.shared.dataTask(with: url)
{ (data, response, error) in
if error == nil
{
print("請(qǐng)求網(wǎng)絡(luò)成功:\(String(describing: response))" )
}
}.resume()
輸出結(jié)果為:
請(qǐng)求網(wǎng)絡(luò)成功:Optional(<NSHTTPURLResponse: 0x600001b1ee20> { URL: https://www.baidu.com/ } { Status Code: 200, Headers {
"Content-Encoding" = (
gzip
);
"Content-Length" = (
1145
);
"Content-Type" = (
"text/html"
);
Date = (
"Thu, 21 Jan 2021 07:15:16 GMT"
);
Server = (
bfe
);
} })
b淫茵、區(qū)別Configuration中的default與ephemeral
-
default:默認(rèn)模式爪瓜,通常我們用這種模式就足夠了。
default
模式下系統(tǒng)會(huì)創(chuàng)建一個(gè)持久化的緩存并在用戶的鑰匙串中存儲(chǔ)證書(shū) -
ephemeral:系統(tǒng)沒(méi)有進(jìn)行任何持久性存儲(chǔ)匙瘪,所有內(nèi)容的生命周期都與
session
相同铆铆,當(dāng)session
無(wú)效時(shí),所有內(nèi)容自動(dòng)釋放
let defaultConfiguration = URLSessionConfiguration.default
let ephemeralConfiguration = URLSessionConfiguration.ephemeral
print("default 沙盒大小: \(String(describing: defaultConfiguration.urlCache?.diskCapacity))")
print("default 內(nèi)存大小: \(String(describing: defaultConfiguration.urlCache?.memoryCapacity))")
print("ephemeral 沙盒大小: \(String(describing: ephemeralConfiguration.urlCache?.diskCapacity))")
print("ephemeral 內(nèi)存大小: \(String(describing: ephemeralConfiguration.urlCache?.memoryCapacity))")
從輸出結(jié)果中可以看到ephemeral
的沙盒大小為0丹喻,而default
有一個(gè)沙盒大小算灸,即系統(tǒng)為default
提供了一定大小的沙盒來(lái)存儲(chǔ)證書(shū)等內(nèi)容。
default 沙盒大小: Optional(10000000)
default 內(nèi)存大小: Optional(512000)
ephemeral 沙盒大小: Optional(0)
ephemeral 內(nèi)存大小: Optional(512000)
c驻啤、切換到后臺(tái)停止下載問(wèn)題
-
background
會(huì)創(chuàng)建一個(gè)可以在后臺(tái)甚至APP已經(jīng)關(guān)閉的時(shí)候仍然傳輸數(shù)據(jù)的會(huì)話 -
background
模式與default
模式非常相似菲驴,不過(guò)background
模式會(huì)用一個(gè)獨(dú)立線程來(lái)進(jìn)行數(shù)據(jù)傳輸 -
background
模式可以在程序掛起,退出骑冗,崩潰的情況下赊瞬,在重新啟動(dòng)APP時(shí)繼續(xù)運(yùn)行task
- 可以利用標(biāo)識(shí)符來(lái)進(jìn)行恢復(fù)
task
。后臺(tái)Session
一定要在創(chuàng)建的時(shí)候賦予一個(gè)唯一的identifier
贼涩,這樣在APP下次運(yùn)行的時(shí)候巧涧,能夠根據(jù)identifier
來(lái)進(jìn)行相關(guān)的區(qū)分 - 如果用戶強(qiáng)制關(guān)閉了APP,IOS 系統(tǒng)會(huì)關(guān)閉所有的
background Session
遥倦,只有當(dāng)用戶下次啟動(dòng)了APP谤绳,數(shù)據(jù)傳輸任務(wù)才會(huì)繼續(xù)執(zhí)行
// 配置Configuration
let backgroundConfiguration = URLSessionConfiguration.background(withIdentifier: createID())
// 創(chuàng)建URLSession
let backgroundURLSession = URLSession.init(configuration: backgroundConfiguration, delegate: self, delegateQueue: OperationQueue.main)
// 開(kāi)始下載
backgroundURLSession.downloadTask(with: downloadUrl).resume()
? 下載完成后進(jìn)行沙盒遷移占锯,拷貝下載完成的文件到用戶目錄(文件名以時(shí)間戳命名)
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)
{
print("下載完成后文件位置:\(location)")
let originalLocationPath = location.path
let destinationPath = NSHomeDirectory() + "/Documents/" + currentDateTurnString() + ".mp4"
print("文件移動(dòng)后的位置:\(destinationPath)")
let fileManager = FileManager.default
try! fileManager.moveItem(atPath: originalLocationPath, toPath: destinationPath)
}
輸出結(jié)果為:
下載完成后文件位置:file:///Users/xiejiapei/Library/Developer/CoreSimulator/Devices/9AF7A16E-76FF-4711-8BFA-66DDF38D03F2/data/Containers/Data/Application/67D3E5B6-989E-4137-9ED0-06F852F31CA3/Library/Caches/com.apple.nsurlsessiond/Downloads/com.xiejiapei.UseAlamofire/CFNetworkDownload_LX9nZT.tmp
文件移動(dòng)后的位置:/Users/xiejiapei/Library/Developer/CoreSimulator/Devices/9AF7A16E-76FF-4711-8BFA-66DDF38D03F2/data/Containers/Data/Application/67D3E5B6-989E-4137-9ED0-06F852F31CA3/Documents/20210121145809.mp4
? 計(jì)算下載進(jìn)度
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)
{
print("bytesWritten: \(bytesWritten)\n totalBytesWritten: \(totalBytesWritten)\n totalBytesExpectedToWrite: \(totalBytesExpectedToWrite)")
print("下載進(jìn)度條:\( Double(totalBytesWritten) / Double(totalBytesExpectedToWrite) )")
}
輸出結(jié)果為:
......
bytesWritten: 139246
totalBytesWritten: 17218551
totalBytesExpectedToWrite: 17244422
下載進(jìn)度條:0.998499746758691
bytesWritten: 25871
totalBytesWritten: 17244422
totalBytesExpectedToWrite: 17244422
下載進(jìn)度條:1.0
? 保存后臺(tái)下載時(shí)的completionHandler,用于開(kāi)啟后臺(tái)下載權(quán)限
var backgroundSessionCompletionHandler: (() -> Void)?
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void)
{
self.backgroundSessionCompletionHandler = completionHandler
}
如果不這樣做缩筛,當(dāng)用戶將APP切換到后臺(tái)的時(shí)候下載就會(huì)直接中斷消略,但是當(dāng)回到APP的時(shí)候會(huì)繼續(xù)之前的進(jìn)度進(jìn)行下載。
2021-01-21 15:34:12.825608+0800 UseAlamofire[53235:1879599] BackgroundSession <01EC4B4A-A81E-4D5B-9004-CF615DEDFD87> connection to background transfer daemon interrupted
? 調(diào)用保存的后臺(tái)下載回調(diào)瞎抛,告訴系統(tǒng)及時(shí)更新屏幕
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession)
{
print("讓后臺(tái)任務(wù)保持下載")
DispatchQueue.main.async {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let backgroundHandle = appDelegate.backgroundSessionCompletionHandler else { return }
backgroundHandle()
}
}
輸出結(jié)果為:
讓后臺(tái)任務(wù)保持下載
需要注意的是艺演,當(dāng)我們切換到后臺(tái)的時(shí)候,任務(wù)雖然還在下載桐臊,但是下載進(jìn)度將不會(huì)再打印到控制臺(tái)上胎撤,你不要以為下載終止了(??,我起初就是這樣以為的断凶,還查了些資料)伤提。
2、屬性
常規(guī)屬性
- identifier:配置對(duì)象的后臺(tái)會(huì)話標(biāo)識(shí)符
- httpAdditionalHeaders:與請(qǐng)求一起發(fā)送的附加頭文件的字典
- networkServiceType:網(wǎng)絡(luò)服務(wù)的類(lèi)型
- allowsCellularAccess:一個(gè)布爾值认烁,用于確定是否應(yīng)通過(guò)蜂窩網(wǎng)絡(luò)進(jìn)行連接
- timeoutIntervalForRequest:等待其他數(shù)據(jù)時(shí)使用的超時(shí)間隔
- timeoutIntervalForResource:資源請(qǐng)求應(yīng)該允許的最大時(shí)間量
- sharedContainerIdentifier:應(yīng)該下載后臺(tái)URL會(huì)話中的文件的共享容器的標(biāo)識(shí)符
- waitsForConnectivity:一個(gè)布爾值肿男,指示會(huì)話是否應(yīng)等待連接變?yōu)榭捎没蛘吡⒓词?/li>
設(shè)置Cookie政策
-
httpCookieAcceptPolicy:決定何時(shí)應(yīng)該接受
Cookie
的策略常量 -
httpShouldSetCookies:一個(gè)布爾值,用于確定請(qǐng)求是否應(yīng)包含來(lái)自
Cookie
存儲(chǔ)的內(nèi)容 -
httpCookieStorage:管理
cookie
存儲(chǔ)的單一對(duì)象(共享實(shí)例) -
HTTPCookie:表示
HTTP cookie
的對(duì)象砚著。它是一個(gè)不可變的對(duì)象次伶,從包含cookie
屬性的字典中初始化
設(shè)置安全策略
-
tlsMaximumSupportedProtocol:在此會(huì)話中進(jìn)行連接時(shí)客戶端應(yīng)請(qǐng)求的最大
TLS
協(xié)議版本 -
tlsMinimumSupportedProtocol:協(xié)議協(xié)商期間應(yīng)該接受的最小
TLS
協(xié)議 - urlCredentialStorage:提供身份驗(yàn)證憑據(jù)的憑證存儲(chǔ)
設(shè)置緩存策略
-
urlCache:用于向會(huì)話中的請(qǐng)求提供緩存響應(yīng)的
URL
緩存 - requestCachePolicy:一個(gè)預(yù)定義常量,用于確定何時(shí)從緩存中返回響應(yīng)
支持后臺(tái)轉(zhuǎn)移
- sessionSendsLaunchEvents:一個(gè)布爾值稽穆,指示在傳輸完成時(shí)是否應(yīng)該在后臺(tái)繼續(xù)或啟動(dòng)應(yīng)用程序
- isDiscretionary:一個(gè)布爾值冠王,用于確定是否可以根據(jù)系統(tǒng)的判斷來(lái)調(diào)度后臺(tái)任務(wù)以獲得最佳性能
支持自定義協(xié)議
- protocolClasses:在會(huì)話中處理請(qǐng)求的額外協(xié)議子類(lèi)的數(shù)組
-
URLProtocol:一個(gè)
NSURLProtocol
對(duì)象處理加載協(xié)議特定的URL
數(shù)據(jù)。NSURLProtocol
類(lèi)本身是一個(gè)抽象類(lèi)舌镶,可以為與特定URL
方案的URL
處理基礎(chǔ)設(shè)施柱彻。您可以為您的應(yīng)用支持的任何自定義協(xié)議或URL
方案創(chuàng)建子類(lèi)
支持多路徑TCP
-
multipathServiceType:指定用于通過(guò)Wi-Fi和蜂窩接口傳輸數(shù)據(jù)的多路徑
TCP
連接策略的服務(wù)類(lèi)型 -
URLSessionConfiguration.MultipathServiceType:指定多路徑
TCP
使用的服務(wù)類(lèi)型的常量
設(shè)置HTTP策略和代理屬性
- httpMaximumConnectionsPerHost:同時(shí)連接到給定主機(jī)的最大數(shù)量
- httpShouldUsePipelining:一個(gè)布爾值,用于確定會(huì)話是否應(yīng)使用HTTP流水線
- connectionProxyDictionary:包含有關(guān)在此會(huì)話中使用的代理信息的字典
支持連接變化
- waitsForConnectivity:一個(gè)布爾值餐胀,指示會(huì)話是否應(yīng)等待連接變?yōu)榭捎没蛘吡⒓词?/li>
默認(rèn)緩存策略
-
NSURLRequestUseProtocolCachePolicy = 0:如果請(qǐng)求擁有一個(gè)緩存的響應(yīng)哟楷,那么
URL
加載系統(tǒng)會(huì)檢查這個(gè)響應(yīng)來(lái)決定。假如內(nèi)容必須重新生效否灾,將建立一個(gè)連向源端的連接來(lái)查看內(nèi)容是否發(fā)生變化卖擅。假如內(nèi)容沒(méi)有變化,那么響應(yīng)就從本地緩存返回?cái)?shù)據(jù)墨技。如果內(nèi)容變化了惩阶,那么數(shù)據(jù)將從源端獲取。
3扣汪、HTTP
a断楷、三次握手
為什么不是兩次握手?
沒(méi)有第三次握手崭别,服務(wù)端就不知道客戶端是否可以接收到消息冬筒,即確定服務(wù)端和客戶端兩者都可以發(fā)送消息也可以接收消息恐锣。
為什么不是四次握手?
通過(guò)三次握手已經(jīng)確定服務(wù)端和客戶端兩者都可以發(fā)送消息也可以接收消息了舞痰,沒(méi)必要再進(jìn)行第四次握手土榴。
b、四次揮手
為什么不是兩次揮手匀奏?
因?yàn)榉?wù)器需要通過(guò)第三次揮手將還沒(méi)有傳輸完成的數(shù)據(jù)全部傳輸完成鞭衩。
為什么不是三次揮手学搜?
因?yàn)榉?wù)器需要通過(guò)第三次揮手將還沒(méi)有傳輸完成的數(shù)據(jù)全部傳輸完成娃善,而客戶端需要通過(guò)第四次揮手告訴服務(wù)端第三次揮手發(fā)送過(guò)來(lái)的數(shù)據(jù)已經(jīng)全部接收完畢,通過(guò)四次揮手可以保證整個(gè)通訊過(guò)程的完整性瑞佩。
c聚磺、使用Wireshark工具抓包
? 兩個(gè)終端之間達(dá)到握手
當(dāng)在下圖左側(cè)的終端輸入的時(shí)候會(huì)自動(dòng)顯示到右側(cè)終端進(jìn)行同步,即兩個(gè)終端之間成功達(dá)到了握手炬丸。
? 進(jìn)入Loopback界面
? 在終端重新進(jìn)行握手讓Loopback開(kāi)始監(jiān)聽(tīng)
? 監(jiān)聽(tīng)到三次握手流程中Seq和Ack的變化
? 在終端斷掉握手讓Loopback監(jiān)聽(tīng)四次揮手
? 監(jiān)聽(tīng)到四次揮手流程中Seq和Ack的變化
d瘫寝、OSL七層協(xié)議
- 應(yīng)用層(HTTP、FTP稠炬、DNS):文件傳輸焕阿,電子郵件,文件服務(wù)首启,虛擬終端
- 表示層(沒(méi)有協(xié)議):數(shù)據(jù)格式化暮屡,代碼轉(zhuǎn)換,數(shù)據(jù)加密
- 會(huì)話層(沒(méi)有協(xié)議):解除或建立與別的接點(diǎn)的聯(lián)系
- 傳輸層(TCP毅桃、UDP):提供端對(duì)端的接口
- 網(wǎng)絡(luò)層(IP):為數(shù)據(jù)包選擇路由
- 數(shù)據(jù)鏈路層(SLIP):傳輸有地址的幀以及錯(cuò)誤檢測(cè)功能
- 物理層(ISO2110):以二進(jìn)制數(shù)據(jù)形式在物理媒體上傳輸數(shù)據(jù)
七層協(xié)議
包裝與解包
二褒纲、初涉Alamofire
Alamofire
為 HTTP
網(wǎng)絡(luò)請(qǐng)求提供了一個(gè)優(yōu)雅且可組合的接口。它沒(méi)有實(shí)現(xiàn)自己的 HTTP
網(wǎng)絡(luò)功能钥飞。取而代之的是莺掠,它建立在由Foundation
框架提供的URL
加載系統(tǒng)之上。系統(tǒng)的核心是 URLSession
和 URLSessionTask
子類(lèi)读宙。
1彻秆、發(fā)起請(qǐng)求
Alamofire
為發(fā)出 HTTP
請(qǐng)求提供了多種方便的方法。
a结闸、最簡(jiǎn)單的請(qǐng)求方式只需提供一個(gè)可以轉(zhuǎn)換為 URL 的 String
- 鏈?zhǔn)秸Z(yǔ)法
- 返回
JSON
- 直接放入
String
唇兑,而不是URL
AF.request("https://httpbin.org/get").response
{ response in
debugPrint(response)
}
輸出結(jié)果為:
[Request]: GET https://httpbin.org/get
[Headers]: None
[Body]: None
[Response]:
[Status Code]: 200
[Headers]:
access-control-allow-credentials: true
Access-Control-Allow-Origin: *
Content-Length: 426
Content-Type: application/json
Date: Mon, 25 Jan 2021 05:35:54 GMT
Server: gunicorn/19.9.0
[Body]:
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "br;q=1.0, gzip;q=0.9, deflate;q=0.8",
"Accept-Language": "en;q=1.0",
"Host": "httpbin.org",
"User-Agent": "UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1",
"X-Amzn-Trace-Id": "Root=1-600e58ba-22848fce5ae105571e598f1e"
},
"origin": "222.76.251.163",
"url": "https://httpbin.org/get"
}
[Network Duration]: 1.434872031211853s
[Serialization Duration]: 0.0s
[Result]: success(Optional(426 bytes))
這實(shí)際上是一種縮寫(xiě)形式,它的完整定義如下膀估。此方法創(chuàng)建一個(gè) DataRequest
幔亥,允許傳入多個(gè)參數(shù)。
open func request<Parameters: Encodable>(
_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil
) -> DataRequest
b察纯、為遵循 Alamofire 的 URLRequestConvertible 協(xié)議的任何類(lèi)型創(chuàng)建 DataRequest
open func request(
_ urlRequest: URLRequestConvertible,
interceptor: RequestInterceptor? = nil
) -> DataRequest
2帕棉、HTTP Methods
a针肥、作為 method 參數(shù)傳遞給 AF.request API
不同的 HTTP
方法可能有不同的語(yǔ)義,需要不同的參數(shù)編碼香伴,這取決于服務(wù)器的期望慰枕。例如,URLSession
或 Alamofire
不支持在 GET
請(qǐng)求中傳遞 body
數(shù)據(jù)即纲,否則將返回錯(cuò)誤具帮。
public struct HTTPMethod: RawRepresentable, Equatable, Hashable
{
public static let connect = HTTPMethod(rawValue: "CONNECT")
public static let delete = HTTPMethod(rawValue: "DELETE")
public static let get = HTTPMethod(rawValue: "GET")
public static let head = HTTPMethod(rawValue: "HEAD")
public static let options = HTTPMethod(rawValue: "OPTIONS")
public static let patch = HTTPMethod(rawValue: "PATCH")
public static let post = HTTPMethod(rawValue: "POST")
public static let put = HTTPMethod(rawValue: "PUT")
public static let trace = HTTPMethod(rawValue: "TRACE")
public let rawValue: String
public init(rawValue: String)
{
self.rawValue = rawValue
}
}
這些值可以作為 method
參數(shù)傳遞給 AF.request API
。
AF.request("https://httpbin.org/get")
AF.request("https://httpbin.org/post", method: .post)
AF.request("https://httpbin.org/put", method: .put)
AF.request("https://httpbin.org/delete", method: .delete)
b低斋、Alamofire 還提供了對(duì) URLRequest 的擴(kuò)展
以橋接將字符串返回到 HTTPMethod
值的 httpMethod
屬性蜂厅。
extension URLRequest
{
/// Returns the `httpMethod` as Alamofire's `HTTPMethod` type.
public var method: HTTPMethod?
{
get { httpMethod.flatMap(HTTPMethod.init) }
set { httpMethod = newValue?.rawValue }
}
}
c、擴(kuò)展HTTPMethod類(lèi)型添加自定義值
如果需要使用 Alamofire
的 HTTPMethod
類(lèi)型不支持的 HTTP
方法膊畴,可以擴(kuò)展該類(lèi)型以添加自定義值掘猿。
extension HTTPMethod
{
static let custom = HTTPMethod(rawValue: "CUSTOM")
}
3、請(qǐng)求參數(shù)和參數(shù)編碼器
a唇跨、請(qǐng)求參數(shù)
Alamofire
支持將遵守 Encodable
協(xié)議的類(lèi)型作為請(qǐng)求參數(shù)稠通。這些請(qǐng)求參數(shù)通過(guò)遵循 ParameterEncoder
協(xié)議的參數(shù)編碼器進(jìn)行傳遞并添加到 URLRequest
中,最后通過(guò)網(wǎng)絡(luò)發(fā)送买猖。Alamofire
包含兩種遵循 ParameterEncoder
協(xié)議的參數(shù)編碼器:JSONParameterEncoder
和 URLEncodedFormParameterEncoder
改橘。這兩種參數(shù)編碼器涵蓋了最常見(jiàn)的編碼方式。
struct Login: Encodable
{
let email: String
let password: String
}
let login = Login(email: "2170928274@qq.com", password: "19970118")
AF.request("https://httpbin.org/post", method: .post, parameters: login, encoder: JSONParameterEncoder.default).response
{ response in
debugPrint(response)
}
輸出結(jié)果為:
[Request]: POST https://httpbin.org/post
[Headers]:
Content-Type: application/json
[Body]:
{"email":"2170928274@qq.com","password":"19970118"}
[Response]:
[Status Code]: 200
[Headers]:
access-control-allow-credentials: true
Access-Control-Allow-Origin: *
Content-Length: 682
Content-Type: application/json
Date: Mon, 25 Jan 2021 06:01:51 GMT
Server: gunicorn/19.9.0
[Body]:
{
"args": {},
"data": "{\"email\":\"2170928274@qq.com\",\"password\":\"19970118\"}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "br;q=1.0, gzip;q=0.9, deflate;q=0.8",
"Accept-Language": "en;q=1.0",
"Content-Length": "51",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1",
"X-Amzn-Trace-Id": "Root=1-600e5ecf-01e2ada305b5bd8a0e0e0dc6"
},
"json": {
"email": "2170928274@qq.com",
"password": "19970118"
},
"origin": "222.76.251.163",
"url": "https://httpbin.org/post"
}
[Network Duration]: 2.090991973876953s
[Serialization Duration]: 0.0s
[Result]: success(Optional(682 bytes))
b玉控、使用 URL 編碼參數(shù)的 GET 請(qǐng)求(默認(rèn)編碼方式)
// https://httpbin.org/get?foo=bar
let parameters = ["foo": "bar"]
// 下面三種方法都是等價(jià)的
AF.request("https://httpbin.org/get", parameters: parameters)
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/get", parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .methodDependent))
輸出結(jié)果為:
[Request]: GET https://httpbin.org/get?foo=bar
c飞主、使用 URL 編碼參數(shù)的 POST 請(qǐng)求
// HTTP body: "qux[]=x&qux[]=y&qux[]=z&baz[]=a&baz[]=b&foo[]=bar"
let parameters: [String: [String]] =
[
"foo": ["bar"],
"baz": ["a", "b"],
"qux": ["x", "y", "z"]
]
// 下面三種方法都是等價(jià)的
AF.request("https://httpbin.org/post", method: .post, parameters: parameters)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .httpBody))
輸出結(jié)果為:
[Request]: POST https://httpbin.org/post
[Headers]:
Content-Type: application/x-www-form-urlencoded; charset=utf-8
[Body]: 73 bytes
"form": {
"baz[]": [
"a",
"b"
],
"foo[]": "bar",
"qux[]": [
"x",
"y",
"z"
]
},
d、JSON 編碼參數(shù)的 POST 請(qǐng)求
// HTTP body: {"baz":["a","b"],"foo":["bar"],"qux":["x","y","z"]}
let parameters: [String: [String]] =
[
"foo": ["bar"],
"baz": ["a", "b"],
"qux": ["x", "y", "z"]
]
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: Alamofire.JSONParameterEncoder.default).response
{ response in
debugPrint(response)
}
輸出結(jié)果為:
[Request]: POST https://httpbin.org/post
[Headers]:
Content-Type: application/json
[Body]:
{"foo":["bar"],"qux":["x","y","z"],"baz":["a","b"]}a
4奸远、HTTP Headers
a既棺、構(gòu)造HTTP Headers
Alamofire
包含自己的 HTTPHeaders
類(lèi)型,這是一種保持順序且不區(qū)分大小寫(xiě)的 name
/value
對(duì)的表示懒叛。HTTPHeader
類(lèi)型可以封裝單個(gè) name
/value
對(duì)丸冕,并為常用的 headers
提供各種靜態(tài)值。向 Request
添加自定義 HTTPHeaders
就像向 request
方法傳遞值一樣簡(jiǎn)單薛窥。
let headers: HTTPHeaders =
[
"Authorization": "Basic VXNlcm5hbWU6UGFzc3dvcmQ=",
"Accept": "application/json"
]
AF.request("https://httpbin.org/headers", headers: headers).responseJSON
{ response in
debugPrint(response)
}
HTTPHeaders
類(lèi)型也可以采用如下方式進(jìn)行構(gòu)造:
let anotherHeaders: HTTPHeaders =
[
.authorization(username: "Username", password: "Password"),
.accept("application/json")
]
b胖烛、Session 為每個(gè) Request 提供一組默認(rèn)的 headers
-
Accept-Encoding:默認(rèn)為
"br;q=1.0, gzip;q=0.9, deflate;q=0.8"
-
Accept-Language:默認(rèn)系統(tǒng)中最多含有 6 種首選語(yǔ)言,格式為
"en;q=1.0"
-
User-Agent:包含有關(guān)應(yīng)用程序的版本信息诅迷,例如
"UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1"
5佩番、響應(yīng)驗(yàn)證
默認(rèn)情況下,無(wú)論響應(yīng)的內(nèi)容如何罢杉,Alamofire
都會(huì)將任何已完成的請(qǐng)求視為成功趟畏。如果響應(yīng)具有不可接受的狀態(tài)代碼或 MIME
類(lèi)型,則在響應(yīng)處理程序之前調(diào)用 validate()
將導(dǎo)致生成錯(cuò)誤滩租。
a赋秀、自動(dòng)驗(yàn)證
validate()
會(huì)自動(dòng)驗(yàn)證狀態(tài)代碼是否在200..<300
范圍內(nèi)利朵,以及響應(yīng)的Content-Type header
是否與請(qǐng)求的 Accept
匹配(如果有提供)。
AF.request("https://httpbin.org/get").validate().responseJSON
{ response in
debugPrint(response)
}
輸出結(jié)果為:
[Response]:
[Status Code]: 200
[Headers]:
access-control-allow-credentials: true
Access-Control-Allow-Origin: *
Content-Length: 427
Content-Type: application/json
Date: Mon, 25 Jan 2021 07:38:36 GMT
Server: gunicorn/19.9.0
b猎莲、手動(dòng)驗(yàn)證
AF.request("https://httpbin.org/get")
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseData
{ response in
switch response.result
{
case .success:
print("Validation Successful")
case let .failure(error):
print(error)
}
}
輸出結(jié)果為:
Validation Successful
6绍弟、響應(yīng)處理
不管被序列化成哪種類(lèi)型,結(jié)果都會(huì)通過(guò)閉包的參數(shù)response
返回著洼,如果是被序列化的數(shù)據(jù)樟遣,就通過(guò)resonse
中的result.value
來(lái)獲取數(shù)據(jù)。源碼中response
閉包函數(shù)的返回值是Self
身笤,也就是Request
豹悬,這就讓我們能夠使用鏈?zhǔn)皆L問(wèn)來(lái)做一些很有意思的事情,任務(wù)按照順序依次放入到隊(duì)列中展鸡。
a屿衅、Handler
不計(jì)算任何響應(yīng)數(shù)據(jù)埃难。它只是直接從 URLSessionDelegate
轉(zhuǎn)發(fā)所有信息莹弊。
// 未序列化的 Response
func response(
queue: DispatchQueue = .main,
completionHandler: @escaping (AFDataResponse<Data?>) -> Void
) -> Self
// 序列化的 Response
func response<Serializer: DataResponseSerializerProtocol>(
queue: DispatchQueue = .main,
responseSerializer: Serializer,
completionHandler: @escaping (AFDataResponse<Serializer.SerializedObject>) -> Void
) -> Self
使用方式為:
AF.request("https://httpbin.org/get").response
{ response in
debugPrint("Response: \(response)")
}
輸出結(jié)果為:
"Response: success(Optional(426 bytes))"
b、Data Handler
使用 DataResponseSerializer
提取并驗(yàn)證服務(wù)器返回的數(shù)據(jù)涡尘。如果沒(méi)有發(fā)生錯(cuò)誤并且返回?cái)?shù)據(jù)忍弛,則響應(yīng)結(jié)果將為 .success
, value
將為從服務(wù)器返回的 Data
考抄。
func responseData(
queue: DispatchQueue = .main,
completionHandler: @escaping (AFDataResponse<Data>) -> Void
) -> Self
使用方式為:
AF.request("https://httpbin.org/get").responseData
{ response in
debugPrint("Response: \(response)")
}
輸出結(jié)果為:
"Response: success(427 bytes)"
c细疚、String Handler
使用 StringResponseSerializer
將服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)換為具有指定編碼的String
。如果沒(méi)有發(fā)生錯(cuò)誤川梅,并且服務(wù)器數(shù)據(jù)成功序列化為 String
疯兼,則響應(yīng)結(jié)果將為 .success
,并且值的類(lèi)型為 String
贫途。
func responseString(
queue: DispatchQueue = .main,
encoding: String.Encoding? = nil,
completionHandler: @escaping (AFDataResponse<String>) -> Void
) -> Self
使用方式為:
AF.request("https://httpbin.org/get").responseString
{ response in
debugPrint("Response: \(response)")
}
輸出結(jié)果為:
"Response: success(\"{\\n \\\"args\\\": {}, \\n \\\"headers\\\": {\\n \\\"Accept\\\": \\\"*/*\\\", \\n \\\"Accept-Encoding\\\": \\\"br;q=1.0, gzip;q=0.9, deflate;q=0.8\\\", \\n \\\"Accept-Language\\\": \\\"en;q=1.0\\\", \\n \\\"Host\\\": \\\"httpbin.org\\\", \\n \\\"User-Agent\\\": \\\"UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1\\\", \\n \\\"X-Amzn-Trace-Id\\\": \\\"Root=1-600e8ac6-70026c993647252b60805135\\\"\\n }, \\n \\\"origin\\\": \\\"218.104.139.115\\\", \\n \\\"url\\\": \\\"https://httpbin.org/get\\\"\\n}\\n\")"
d吧彪、JSON Handler
使用指定的 JSONSerialization.ReadingOptions
將服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)換為 Any
類(lèi)型。如果沒(méi)有出現(xiàn)錯(cuò)誤丢早,并且服務(wù)器數(shù)據(jù)成功序列化為 JSON
對(duì)象姨裸,則響應(yīng) AFResult
將為 .success
,值將為 Any
類(lèi)型怨酝。
func responseJSON(
queue: DispatchQueue = .main,
options: JSONSerialization.ReadingOptions = .allowFragments,
completionHandler: @escaping (AFDataResponse<Any>) -> Void
) -> Self
使用方式為:
AF.request("https://httpbin.org/get").responseJSON
{ response in
debugPrint("Response: \(response)")
}
輸出結(jié)果為:
"Response: success({\n args = {\n };\n headers = {\n Accept = \"*/*\";\n \"Accept-Encoding\" = \"br;q=1.0, gzip;q=0.9, deflate;q=0.8\";\n \"Accept-Language\" = \"en;q=1.0\";\n Host = \"httpbin.org\";\n \"User-Agent\" = \"UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1\";\n \"X-Amzn-Trace-Id\" = \"Root=1-600e8b4a-28fe0a1064c7a6e1033414fa\";\n };\n origin = \"218.104.139.115\";\n url = \"https://httpbin.org/get\";\n})"
e傀缩、可解碼類(lèi)型的Handler
使用 DecodableResponseSerializer
和 指定的 DataDecoder
(Decoder
的協(xié)議抽象,可以從 Data
解碼)將服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)換為傳遞進(jìn)來(lái)的 Decodable
類(lèi)型农猬。如果沒(méi)有發(fā)生錯(cuò)誤赡艰,并且服務(wù)器數(shù)據(jù)已成功解碼為 Decodable
類(lèi)型,則響應(yīng) Result
將為 .success
斤葱,并且 value
將為傳遞進(jìn)來(lái)的類(lèi)型慷垮。
func responseDecodable<T: Decodable>(
of type: T.Type = T.self,
queue: DispatchQueue = .main,
decoder: DataDecoder = JSONDecoder(),
completionHandler: @escaping (AFDataResponse<T>) -> Void
) -> Self
使用方式為:
struct HTTPBinResponse: Decodable
{
let url: String
}
AF.request("https://httpbin.org/get").responseDecodable(of: HTTPBinResponse.self)
{ response in
debugPrint("Response: \(response)")
}
輸出結(jié)果為:
"Response: success(UseAlamofire.HTTPBinResponse(url: \"https://httpbin.org/get\"))"
f勋又、鏈?zhǔn)巾憫?yīng)
沒(méi)有一個(gè)響應(yīng) handlers
對(duì)從服務(wù)器返回的 HTTPURLResponse
執(zhí)行任何驗(yàn)證换帜。例如惯驼,400..<500
和 500..<600
范圍內(nèi)的響應(yīng)狀態(tài)代碼不會(huì)自動(dòng)觸發(fā)錯(cuò)誤祟牲。Alamofire
使用鏈?zhǔn)降捻憫?yīng)驗(yàn)證來(lái)實(shí)現(xiàn)這一點(diǎn)说贝。對(duì)同一請(qǐng)求使用多個(gè)響應(yīng) handlers
需要多次序列化服務(wù)器數(shù)據(jù)议惰,每個(gè)響應(yīng) handlers
均處理一次。通常應(yīng)避免對(duì)同一請(qǐng)求使用多個(gè)響應(yīng) handlers
乡恕,特別是在生產(chǎn)環(huán)境中言询。
AF.request("https://httpbin.org/get")
.responseString
{ response in
print("Response String: \(String(describing: response.value) )")
}
.responseJSON
{ response in
print("Response JSON: \(String(describing: response.value))")
}
輸出結(jié)果為:
Response String: Optional("{\n \"args\": {}, \n \"headers\": {\n \"Accept\": \"*/*\", \n \"Accept-Encoding\": \"br;q=1.0, gzip;q=0.9, deflate;q=0.8\", \n \"Accept-Language\": \"en;q=1.0\", \n \"Host\": \"httpbin.org\", \n \"User-Agent\": \"UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1\", \n \"X-Amzn-Trace-Id\": \"Root=1-600e8e25-66ed7b9b0986f02971550391\"\n }, \n \"origin\": \"218.104.139.115\", \n \"url\": \"https://httpbin.org/get\"\n}\n")
Response JSON: Optional({
args = {
};
headers = {
Accept = "*/*";
"Accept-Encoding" = "br;q=1.0, gzip;q=0.9, deflate;q=0.8";
"Accept-Language" = "en;q=1.0";
Host = "httpbin.org";
"User-Agent" = "UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1";
"X-Amzn-Trace-Id" = "Root=1-600e8e25-66ed7b9b0986f02971550391";
};
origin = "218.104.139.115";
url = "https://httpbin.org/get";
})
g、響應(yīng)隊(duì)列
默認(rèn)情況下傲宜,傳遞給響應(yīng) handler
的閉包在 .main
隊(duì)列上執(zhí)行运杭,但可以傳遞一個(gè)指定的 DispatchQueue
來(lái)執(zhí)行閉包。實(shí)際的序列化工作(將 Data
轉(zhuǎn)換為其他類(lèi)型)總是在后臺(tái)隊(duì)列上執(zhí)行函卒。
let utilityQueue = DispatchQueue.global(qos: .utility)
AF.request("https://httpbin.org/get").responseJSON(queue: utilityQueue)
{ response in
print("在全局隊(duì)列上執(zhí)行此網(wǎng)絡(luò)請(qǐng)求:\(Thread.current)")
debugPrint(response)
}
輸出結(jié)果為:
在全局隊(duì)列上執(zhí)行此網(wǎng)絡(luò)請(qǐng)求:<NSThread: 0x600000401a00>{number = 8, name = (null)}
7辆憔、身份驗(yàn)證
a、自動(dòng)提供 URLCredential
Request
的 authenticate
方法將在使用 URLAuthenticationChallenge
進(jìn)行質(zhì)詢時(shí)自動(dòng)提供 URLCredential
报嵌。
let user = "user"
let password = "password"
AF.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(username: user, password: password)
.responseJSON
{ response in
debugPrint(response)
}
輸出結(jié)果為:
[Response]:
[Status Code]: 200
[Headers]:
access-control-allow-credentials: true
Access-Control-Allow-Origin: *
Content-Length: 47
Content-Type: application/json
Date: Mon, 25 Jan 2021 10:01:37 GMT
Server: gunicorn/19.9.0
[Body]:
{
"authenticated": true,
"user": "user"
}
b虱咧、自己提供 URLCredential 進(jìn)行驗(yàn)證
let user = "user"
let password = "password"
let credential = URLCredential(user: user, password: password, persistence: .forSession)
AF.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(with: credential)
.responseJSON
{ response in
debugPrint(response)
}
輸出結(jié)果同上。
8锚国、下載文件
a逸雹、下載數(shù)據(jù)到文件中
除了將數(shù)據(jù)提取到內(nèi)存中之外帖世,Alamofire
還提供了 Session.download
、DownloadRequest
和 DownloadResponse<Success盈魁,F(xiàn)ailure:Error>
以方便下載數(shù)據(jù)到磁盤(pán)飘痛。雖然下載到內(nèi)存中對(duì)小負(fù)載(如大多數(shù) JSON API
響應(yīng))非常有用,但獲取更大的資源(如圖像和視頻)應(yīng)下載到磁盤(pán)竹祷,以避免應(yīng)用程序出現(xiàn)內(nèi)存問(wèn)題践险。DownloadRequest
具有與 DataRequest
相同的大多數(shù)響應(yīng) handlers
鳍刷。但是,由于它將數(shù)據(jù)下載到磁盤(pán),因此序列化響應(yīng)涉及從磁盤(pán)讀取负芋,還可能涉及將大量數(shù)據(jù)讀入內(nèi)存。在設(shè)計(jì)下載處理時(shí),記住這些事實(shí)是很重要的奶镶。
AF.download("https://httpbin.org/image/png").responseData
{ response in
if let data = response.value
{
let image = UIImage(data: data)
self.imageView.image = image!
}
}
b捺信、下載文件的存放位置
所有下載的數(shù)據(jù)最初都存儲(chǔ)在系統(tǒng)臨時(shí)目錄中掌挚。它最終會(huì)在將來(lái)的某個(gè)時(shí)候被系統(tǒng)刪除,所以如果它需要更長(zhǎng)的壽命,將文件移到其他地方是很重要的。我們可以提供 Destination
閉包懊纳,將文件從臨時(shí)目錄移動(dòng)到最終的存放位置萍倡。在臨時(shí)文件實(shí)際移動(dòng)到 destinationURL
之前,將執(zhí)行閉包中指定的 Options
翩蘸。當(dāng)前支持的兩個(gè) Options
是:.createIntermediateDirectories
如果指定郎任,則為目標(biāo) URL
創(chuàng)建中間目錄车猬。.removePreviousFile
如果指定,則從目標(biāo) URL
中刪除以前的文件阅仔。
AF.download("https://httpbin.org/image/png", to: destination).response
{ response in
debugPrint(response)
if response.error == nil, let imagePath = response.fileURL?.path
{
let image = UIImage(contentsOfFile: imagePath)
self.imageView.image = image!
}
}
輸出結(jié)果為:
[Request]: GET https://httpbin.org/image/png
[Headers]: None
[Body]: None
[Response]:
[Status Code]: 200
[Headers]:
access-control-allow-credentials: true
Access-Control-Allow-Origin: *
Content-Length: 8090
Content-Type: image/png
Date: Mon, 25 Jan 2021 10:14:50 GMT
Server: gunicorn/19.9.0
[File URL]: /Users/xiejiapei/Library/Developer/CoreSimulator/Devices/9AF7A16E-76FF-4711-8BFA-66DDF38D03F2/data/Containers/Data/Application/730436F5-DE88-42CA-96AF-B5FC5A4C9019/Documents/image.png
[Resume Data]: None
[Network Duration]: 1.8333059549331665s
[Serialization Duration]: 0.0s
[Result]: success(Optional(file:///Users/xiejiapei/Library/Developer/CoreSimulator/Devices/9AF7A16E-76FF-4711-8BFA-66DDF38D03F2/data/Containers/Data/Application/730436F5-DE88-42CA-96AF-B5FC5A4C9019/Documents/image.png))
文件存儲(chǔ)的位置為
還可以使用建議的文件存儲(chǔ)位置,效果同上。
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
AF.download("https://httpbin.org/image/png", to: destination).response
{ response in
debugPrint(response)
if response.error == nil, let imagePath = response.fileURL?.path
{
let image = UIImage(contentsOfFile: imagePath)
self.imageView.image = image!
}
}
c抖甘、下載進(jìn)度
任何 DownloadRequest
都可以使用 downloadProgress
報(bào)告下載進(jìn)度。只有在服務(wù)器正確返回可用于計(jì)算進(jìn)度的 Content-Length header
時(shí)才能工作椒涯。如果沒(méi)有這個(gè) header
回梧,進(jìn)度將保持在 0.0废岂,直到下載完成帜矾,此時(shí)進(jìn)度將跳到 1.0锌唾。還可以接收一個(gè)queue
參數(shù)具被,該參數(shù)定義應(yīng)該對(duì)哪個(gè) DispatchQueue
調(diào)用下載進(jìn)度閉包矾端。
let utilityQueue = DispatchQueue.global(qos: .utility)
AF.download("https://httpbin.org/image/png")
.downloadProgress(queue: utilityQueue)
{ progress in
print("下載進(jìn)度: \(progress.fractionCompleted)")
}
.responseData
{ response in
if let data = response.value
{
let image = UIImage(data: data)
self.imageView.image = image!
}
}
輸出結(jié)果為:
下載進(jìn)度: 1.0
d凡壤、取消和恢復(fù)下載
除了所有請(qǐng)求類(lèi)都有 cancel()
方法外厨诸,DownloadRequest
還可以生成恢復(fù)數(shù)據(jù)搅方,這些數(shù)據(jù)可以用于以后恢復(fù)下載。此 API 有兩種形式:1)cancel(producingResumeData: Bool)
,它允許控制是否生成恢復(fù)數(shù)據(jù)骚亿,但僅在 DownloadResponse
可用泥技;2)cancel(byProducingResumeData: (_ resumeData: Data?) -> Void)
,它執(zhí)行相同的操作功茴,但恢復(fù)數(shù)據(jù)在 completion handler
中可用衡载。如果DownloadRequest
被取消或中斷晴股,則底層的 URLSessionDownloadTask
可能會(huì)生成恢復(fù)數(shù)據(jù)。如果發(fā)生這種情況狗唉,可以重新使用恢復(fù)數(shù)據(jù)來(lái)重新啟動(dòng)停止的 DownloadRequest
初烘。
let download = AF.download("https://httpbin.org/image/png")
var resumeData: Data!
// 正常下載
download.responseData
{ response in
if let data = response.value
{
let image = UIImage(data: data)
self.imageView.image = image!
}
}
// 從cancel的回調(diào)閉包中獲得resumeData
download.cancel
{ data in
resumeData = data
}
// 使用resumeData繼續(xù)下載
AF.download(resumingWith: resumeData).responseData
{ response in
if let data = response.value
{
let image = UIImage(data: data)
self.imageView.image = image!
}
}
9、上傳數(shù)據(jù)到服務(wù)器
當(dāng)使用 JSON
或 URL
編碼的參數(shù)向服務(wù)器發(fā)送相對(duì)少量的數(shù)據(jù)時(shí)分俯,request()
通常就足夠了肾筐。如果需要從內(nèi)存、文件 URL
或 InputStream
中的 Data
發(fā)送大量數(shù)據(jù)缸剪,那么 upload()
就是您想要使用的吗铐。
a、上傳 Data
let data = Data("XieJiaPei".utf8)
AF.upload(data, to: "https://httpbin.org/post").responseJSON
{ response in
debugPrint(response)
}
輸出結(jié)果為:
[Result]: success({
args = {
};
data = "";
files = {
};
form = {
XieJiaPei = "";
};
headers = {
Accept = "*/*";
"Accept-Encoding" = "br;q=1.0, gzip;q=0.9, deflate;q=0.8";
"Accept-Language" = "en;q=1.0";
"Content-Length" = 9;
"Content-Type" = "application/x-www-form-urlencoded";
Host = "httpbin.org";
"User-Agent" = "UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1";
"X-Amzn-Trace-Id" = "Root=1-600f6966-48de0e395d96f17f396e2b67";
};
json = "<null>";
origin = "222.76.251.163";
url = "https://httpbin.org/post";
})
b杏节、上傳多表單數(shù)據(jù)
AF.upload(multipartFormData: { multipartFormData in
multipartFormData.append(Data("Boy".utf8), withName: "JiaPei")
multipartFormData.append(Data("Girl".utf8), withName: "YuQing")
}, to: "https://httpbin.org/post")
.responseJSON { response in
debugPrint(response)
}
輸出結(jié)果為:
[Result]: success({
args = {
};
data = "";
files = {
};
form = {
JiaPei = Boy;
YuQing = Girl;
};
headers = {
Accept = "*/*";
"Accept-Encoding" = "br;q=1.0, gzip;q=0.9, deflate;q=0.8";
"Accept-Language" = "en;q=1.0";
"Content-Length" = 228;
"Content-Type" = "multipart/form-data; boundary=alamofire.boundary.6d21176fdb63050f";
Host = "httpbin.org";
"User-Agent" = "UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1";
"X-Amzn-Trace-Id" = "Root=1-600f6d84-25a167ab5ddb02c206ff4697";
};
json = "<null>";
origin = "218.104.139.115";
url = "https://httpbin.org/post";
})
c唬渗、上傳文件
let fileURL = Bundle.main.url(forResource: "girl", withExtension: "mp4")!
AF.upload(fileURL, to: "https://httpbin.org/post").responseJSON
{ response in
debugPrint(response)
}
輸出結(jié)果為:
無(wú)
d、上傳進(jìn)度
當(dāng)用戶等待上傳完成時(shí)奋渔,有時(shí)向用戶顯示上傳的進(jìn)度會(huì)很方便镊逝。任何 UploadRequest
都可以使用 uploadProgress
和 downloadProgress
報(bào)告響應(yīng)數(shù)據(jù)下載的上傳進(jìn)度和下載進(jìn)度。
let fileURL = Bundle.main.url(forResource: "girl", withExtension: "mp4")!
if FileManager.default.fileExists(atPath: fileURL.path)
{
AF.upload(fileURL, to: "https://httpbin.org/post")
.uploadProgress
{ progress in
print("上傳進(jìn)度: \(progress.fractionCompleted)")
}
.responseJSON
{ response in
print("上傳完成")
print(response)
}
}
else
{
print("沒(méi)有找到文件")
}
輸出結(jié)果為:
上傳進(jìn)度: 0.01685915418681258
上傳進(jìn)度: 0.5394929339780026
上傳進(jìn)度: 0.6743661674725031
上傳進(jìn)度: 0.9441126344615044
上傳進(jìn)度: 1.0
10嫉鲸、網(wǎng)絡(luò)可達(dá)性
監(jiān)聽(tīng)移動(dòng)網(wǎng)絡(luò)和 WiFi 網(wǎng)絡(luò)接口的主機(jī)和地址的可達(dá)性變化撑蒜。
let manager = NetworkReachabilityManager(host: "www.apple.com")
manager?.startListening
{ status in
print("網(wǎng)絡(luò)狀態(tài)發(fā)生改變: \(status)")
}
輸出結(jié)果為:
網(wǎng)絡(luò)狀態(tài)發(fā)生改變: reachable(Alamofire.NetworkReachabilityManager.NetworkReachabilityStatus.ConnectionType.ethernetOrWiFi)
三、玩轉(zhuǎn)Alamofire
1玄渗、Session
a座菠、Session.default
Alamofire
的 Session
在職責(zé)上大致等同于它維護(hù)的 URLSession
實(shí)例:它提供 API 來(lái)生成各種 Request
子類(lèi),這些子類(lèi)封裝了不同的 URLSessionTask
子類(lèi)藤树,以及封裝應(yīng)用于實(shí)例生成的所有 Request
的各種配置浴滴。Session
提供了一個(gè) default
單例實(shí)例,并且 AF
實(shí)際上就是 Session.default
也榄。因此巡莹,以下兩個(gè)語(yǔ)句是等效的:
AF.request("https://httpbin.org/get").responseJSON
{ response in
debugPrint(response)
}
let session = Session.default
session.request("https://httpbin.org/get").responseJSON
{ response in
debugPrint(response)
}
b、創(chuàng)建自定義的 Session 實(shí)例
使用以下便利初始化器甜紫,并將結(jié)果存儲(chǔ)在整個(gè)應(yīng)用程序使用的單個(gè)實(shí)例中降宅。此初始化器允許自定義 Session
的所有行為。
let session = Session.init(...)
public convenience init
(
configuration: URLSessionConfiguration = URLSessionConfiguration.af.default,
delegate: SessionDelegate = SessionDelegate(),
rootQueue: DispatchQueue = DispatchQueue(label: "org.alamofire.session.rootQueue"),
startRequestsImmediately: Bool = true,
requestQueue: DispatchQueue? = nil,
serializationQueue: DispatchQueue? = nil,
interceptor: RequestInterceptor? = nil,
serverTrustManager: ServerTrustManager? = nil,
redirectHandler: RedirectHandler? = nil,
cachedResponseHandler: CachedResponseHandler? = nil,
eventMonitors: [EventMonitor] = []
)
c囚霸、使用 URLSessionConfiguration 創(chuàng)建 Session
要自定義底層 URLSession
的行為腰根,可以提供自定義的 URLSessionConfiguration
實(shí)例。建議從改變URLSessionConfiguration.af.default
實(shí)例開(kāi)始拓型,因?yàn)樗砑恿?Alamofire
提供的默認(rèn) Accept-Encoding
额嘿、Accept-Language
和User-Agent headers
瘸恼。
let configuration = URLSessionConfiguration.af.default
configuration.allowsCellularAccess = false
let customConfigurationSession = Session(configuration: configuration)
customConfigurationSession.request("https://httpbin.org/get").responseJSON
{ response in
debugPrint(response)
}
d、SessionDelegate
SessionDelegate
實(shí)例封裝了對(duì)各種 URLSessionDelegate
和相關(guān)協(xié)議回調(diào)的所有處理册养。默認(rèn)情況下东帅,Session
將在添加至少一個(gè)響應(yīng) handler
后立即對(duì) Request
調(diào)用 resume()
。將 startRequestsImmediately
設(shè)置為 false
需要手動(dòng)調(diào)用所有請(qǐng)求的 resume()
方法球拦。
let session = Session(startRequestsImmediately: false)
session.request("https://httpbin.org/get").resume().responseJSON
{ response in
debugPrint(response)
}
輸出結(jié)果為:
[Request]: GET https://httpbin.org/get
[Headers]: None
[Body]: None
[Response]: None
[Network Duration]: None
[Serialization Duration]: 0.00031406700145453215s
[Result]: failure(Alamofire.AFError.sessionDeinitialized)
e靠闭、Session 的 DispatchQueue
默認(rèn)情況下,Session
實(shí)例對(duì)所有異步工作使用單個(gè) DispatchQueue
坎炼。這包括 URLSession
的 delegate OperationQueue
的 underlyingQueue
愧膀,用于所有 URLRequest
創(chuàng)建、所有響應(yīng)序列化工作以及所有內(nèi)部 Session
和 Request
狀態(tài)的改變谣光。如果性能分析顯示瓶頸在于 URLRequest
的創(chuàng)建或響應(yīng)序列化檩淋,則可以為 Session
的每個(gè)工作區(qū)域提供單獨(dú)的 DispatchQueue
。提供的任何自定義 rootQueue
都必須是串行隊(duì)列萄金,但 requestQueue
和 serializationQueue
可以是串行或并行隊(duì)列蟀悦。通常建議使用串行隊(duì)列,除非性能分析顯示工作被延遲捡絮,在這種情況下熬芜,使隊(duì)列并行可能有助于提高整體性能。
let rootQueue = DispatchQueue(label: "com.app.session.rootQueue")
let requestQueue = DispatchQueue(label: "com.app.session.requestQueue")
let serializationQueue = DispatchQueue(label: "com.app.session.serializationQueue")
let session = Session(
rootQueue: rootQueue,
requestQueue: requestQueue,
serializationQueue: serializationQueue
)
f福稳、添加其他信息
? 添加 RequestInterceptor
Alamofire
的 RequestInterceptor
協(xié)議(RequestAdapter
& RequestRetrier
)提供了重要而強(qiáng)大的請(qǐng)求自適應(yīng)和重試功能涎拉。
let policy = RetryPolicy()
let session = Session(interceptor: policy)
? 添加 ServerTrustManager
Alamofire
的 ServerTrustManager
類(lèi)封裝了域名和遵循 ServerTrustEvaluating
協(xié)議的類(lèi)型實(shí)例之間的映射,這提供了定制 Session
處理 TLS
安全性的能力的圆。這包括使用證書(shū)和公鑰固定以及證書(shū)吊銷(xiāo)檢查鼓拧。
let manager = ServerTrustManager(evaluators: ["httpbin.org": PinnedCertificatesTrustEvaluator()])
let managerSession = Session(serverTrustManager: manager)
managerSession.request("https://httpbin.org/get").responseJSON
{ response in
debugPrint(response)
}
? 添加 RedirectHandler
Alamofire
的 RedirectHandler
協(xié)議定制了 HTTP
重定向響應(yīng)的處理。
let redirector = Redirector(behavior: .follow)
let redirectorSession = Session(redirectHandler: redirector)
redirectorSession.request("https://httpbin.org/get").responseJSON
{ response in
debugPrint(response)
}
? 添加 CachedResponseHandler
Alamofire
的 CachedResponseHandler
協(xié)議定制了響應(yīng)的緩存越妈,可以在 Session
和 Request
層級(jí)使用季俩。
let cacher = ResponseCacher(behavior: .cache)
let cacherSession = Session(cachedResponseHandler: cacher)
cacherSession.request("https://httpbin.org/get").responseJSON
{ response in
debugPrint(response)
}
? 添加 EventMonitor
Alamofire
的 EventMonitor
協(xié)議提供了對(duì) Alamofire
內(nèi)部事件的強(qiáng)大洞察力。它可以用來(lái)提供日志和其他基于事件的特性梅掠。
let monitor = ClosureEventMonitor()
monitor.requestDidCompleteTaskWithError =
{ (request, task, error) in
debugPrint(request)
}
let monitorSession = Session(eventMonitors: [monitor])
monitorSession.request("https://httpbin.org/get").responseJSON
{ response in
debugPrint(response)
}
g酌住、從 URLSession 創(chuàng)建實(shí)例
除了前面提到的便利初始化器之外,還可以直接從 URLSession
初始化 Session
阎抒。但是酪我,在使用這個(gè)初始化器時(shí)需要記住幾個(gè)要求,因此建議使用便利初始化器且叁。其中包括:
-
Alamofire
不支持為在后臺(tái)使用而配置的URLSession
都哭。初始化Session
時(shí),這將導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。 - 必須創(chuàng)建
SessionDelegate
實(shí)例并將其作為URLSession
的delegate
欺矫,以及傳遞給Session
的初始化器纱新。 - 必須將自定義
OperationQueue
作為URLSession
的delegateQueue
。此隊(duì)列必須是串行隊(duì)列穆趴,它必須具有備用DispatchQueue
脸爱,并且必須將該DispatchQueue
作為其rootQueue
傳遞給Session
。
let rootQueue = DispatchQueue(label: "org.alamofire.customQueue")
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
queue.underlyingQueue = rootQueue
let delegate = SessionDelegate()
let configuration = URLSessionConfiguration.af.default
let urlSession = URLSession(configuration: configuration,
delegate: delegate,
delegateQueue: queue)
let session = Session(session: urlSession, delegate: delegate, rootQueue: rootQueue)
2毡代、Request
a阅羹、創(chuàng)建請(qǐng)求
URLConvertible協(xié)議
可以使用遵循 URLConvertible
協(xié)議的類(lèi)型來(lái)構(gòu)造 URL,然后使用 URL 在內(nèi)部構(gòu)造 URL 請(qǐng)求教寂。默認(rèn)情況下,String
执庐、URL
和URLComponents
遵循了URLConvertible
協(xié)議酪耕,允許將它們中的任何一個(gè)作為 URL 參數(shù)傳遞給 request
、upload
和 download
方法轨淌。
let urlString = "https://httpbin.org/get"
AF.request(urlString)
let url = URL(string: urlString)!
AF.request(url)
let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true)!
AF.request(urlComponents)
URLRequestConvertible協(xié)議
遵循 URLRequestConvertible
協(xié)議的類(lèi)型可用于構(gòu)造 URLRequest
迂烁。默認(rèn)情況下,URLRequest
遵循 URLRequestConvertible
递鹉,允許將其直接傳遞到 request
盟步、upload
和 download
方法中。
let postUrl = URL(string: "https://httpbin.org/post")!
var urlRequest = URLRequest(url: postUrl)
urlRequest.method = .post
let parameters = ["foo": "bar"]
do
{
urlRequest.httpBody = try JSONEncoder().encode(parameters)
}
catch
{
// Handle error.
print("出錯(cuò)了")
}
urlRequest.headers.add(.contentType("application/json"))
AF.request(urlRequest)
.responseJSON
{ response in
debugPrint(response)
}
輸出結(jié)果為:
[Result]: success({
args = {
};
data = "{\"foo\":\"bar\"}";
files = {
};
form = {
};
headers = {
Accept = "*/*";
"Accept-Encoding" = "br;q=1.0, gzip;q=0.9, deflate;q=0.8";
"Accept-Language" = "en;q=1.0";
"Content-Length" = 13;
"Content-Type" = "application/json";
Host = "httpbin.org";
"User-Agent" = "UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1";
"X-Amzn-Trace-Id" = "Root=1-600fbced-3ebfeeb20ee086fa017998b4";
};
json = {
foo = bar;
};
origin = "218.104.139.115";
url = "https://httpbin.org/post";
})
b躏结、請(qǐng)求管道
一旦使用 Request
子類(lèi)的初始參數(shù)或 URLRequestConvertible
創(chuàng)建了它却盘,它就會(huì)通過(guò)組成 Alamofire
請(qǐng)求管道的一系列步驟進(jìn)行傳遞。
請(qǐng)求步驟
- 初始參數(shù)(如
HTTP
方法媳拴、headers
和參數(shù))被封裝到內(nèi)部URLRequestConvertible
值中黄橘。 - 對(duì)
URLRequestConvertible
值調(diào)用asURLRequest()
,創(chuàng)建第一個(gè)URLRequest
值屈溉。此值將傳遞給Request
并存儲(chǔ)在requests
中塞关。 - 如果
Session
有RequestAdapter
或RequestInterceptor
,則使用先前創(chuàng)建的URLRequest
調(diào)用它們子巾。然后將調(diào)整后的URLRequest
傳遞給Request
并存儲(chǔ)在requests
中帆赢。 -
Session
調(diào)用Request
創(chuàng)建的URLSessionTask
,以基于URLRequest
執(zhí)行網(wǎng)絡(luò)請(qǐng)求线梗。 - 完成
URLSessionTask
并收集URLSessionTaskMetrics
(日志)后椰于,Request
將執(zhí)行其Validator
。 - 請(qǐng)求執(zhí)行已附加的任何響應(yīng)
handlers
缠导,如responseDecodable
廉羔。
觸發(fā)重試
在這些步驟中的任何一個(gè),都可以通過(guò)創(chuàng)建或接收的 Error
值來(lái)表示失敗,然后將錯(cuò)誤值傳遞給關(guān)聯(lián)的 Request
憋他。例如孩饼,除了步驟 1 和 4 之外,上面的所有其他步驟都可以創(chuàng)建一個(gè)Error
竹挡,然后傳遞給響應(yīng) handlers
或可供重試镀娶。一旦將錯(cuò)誤傳遞給 Request
,Request
將嘗試運(yùn)行與 Session
或 Request
關(guān)聯(lián)的任何 RequestRetrier
揪罕。如果任何 RequestRetrier
選擇重試該 Request
梯码,則將再次運(yùn)行完整的管道。RequestRetrier
也會(huì)產(chǎn)生 Error
好啰,但這些錯(cuò)誤不會(huì)觸發(fā)重試轩娶。
失敗原因
- 參數(shù)封裝不能失敗。
- 調(diào)用
asURLRequest()
時(shí)框往,任何URLRequestConvertible
值都可能創(chuàng)建錯(cuò)誤鳄抒。這允許初始驗(yàn)證各種URLRequest
屬性或參數(shù)編碼失敗。 -
RequestAdapter
在自適應(yīng)過(guò)程中可能會(huì)失敗椰弊,可能是由于缺少授權(quán)token
许溅。 -
URLSessionTask
創(chuàng)建不能失敗。 -
URLSessionTask
可能由于各種原因帶有錯(cuò)誤地完成秉版,包括網(wǎng)絡(luò)可用性和取消贤重。這些Error
值將傳遞回給Request
。 - 響應(yīng)
handlers
可以產(chǎn)生任何錯(cuò)誤清焕,通常是由于無(wú)效響應(yīng)或其他分析錯(cuò)誤并蝗。
c、請(qǐng)求種類(lèi)
Alamofire
執(zhí)行的每個(gè)請(qǐng)求都由特定的類(lèi)耐朴、DataRequest
借卧、UploadRequest
和 DownloadRequest
封裝。這些類(lèi)中的每一個(gè)都封裝了每種類(lèi)型請(qǐng)求所特有的功能筛峭,但是 DataRequest
和 DownloadRequest
繼承自一個(gè)公共的父類(lèi) Request
(UploadRequest
繼承自 DataRequest
)铐刘。Request
實(shí)例從不直接創(chuàng)建,而是通過(guò)各種 request
方法之一從會(huì)話 Session
中自動(dòng)生成影晓。
DataRequest
DataRequest
是 Request
的一個(gè)子類(lèi)镰吵,它封裝了 URLSessionDataTask
,將服務(wù)器響應(yīng)下載到存儲(chǔ)在內(nèi)存中的 Data
中挂签。因此疤祭,必須認(rèn)識(shí)到,超大下載量可能會(huì)對(duì)系統(tǒng)性能產(chǎn)生不利影響饵婆。對(duì)于這些類(lèi)型的下載勺馆,建議使用 DownloadRequest
將數(shù)據(jù)保存到磁盤(pán)。
除了 Request
提供的屬性之外,DataRequest
還有一些屬性草穆。其中包括 data
(這是服務(wù)器響應(yīng)的累積 Data
)和 convertible
(這是創(chuàng)建 DataRequest
時(shí)使用的 URLRequestConvertible
灌灾,其中包含創(chuàng)建實(shí)例的原始參數(shù))。
默認(rèn)情況下悲柱,DataRequest
不驗(yàn)證響應(yīng)锋喜。相反,必須向其中添加對(duì) validate()
的調(diào)用豌鸡,以驗(yàn)證各種屬性是否有效嘿般。添加 validate()
確保響應(yīng)狀態(tài)代碼在 200..<300
范圍內(nèi),并且響應(yīng)的 Content-Type
與請(qǐng)求的 Accept
匹配涯冠。通過(guò)傳遞 Validation
閉包可以進(jìn)一步定制驗(yàn)證炉奴。
UploadRequest
UploadRequest
是 DataRequest
的一個(gè)子類(lèi),它封裝 URLSessionUploadTask
蛇更、將 Data
盆佣、磁盤(pán)上的文件或 InputStream
上傳到遠(yuǎn)程服務(wù)器。
除了 DataRequest
提供的屬性外械荷,UploadRequest
還有一些屬性。其中包括一個(gè) FileManager
實(shí)例虑灰,用于在上傳文件時(shí)自定義對(duì)磁盤(pán)的訪問(wèn)吨瞎,以及 upload
,upload
封裝了用于描述請(qǐng)求的 URLRequestConvertible
值和確定要執(zhí)行的上傳類(lèi)型的Uploadable
值穆咐。
DownloadRequest
DownloadRequest
是 Request
的一個(gè)具體子類(lèi)颤诀,它封裝了 URLSessionDownloadTask
,將響應(yīng)數(shù)據(jù)下載到磁盤(pán)对湃。DownloadRequest
除了由 Request
提供的屬性外崖叫,還有一些屬性。其中包括取消 DownloadRequest
時(shí)生成的數(shù)據(jù) resumeData
(可用于以后繼續(xù)下載)和 fileURL
(下載完成后下載文件對(duì)應(yīng)的 URL
)拍柒。
除了支持 Request
提供的 cancel()
方法外心傀,DownloadRequest
還包括了其他兩種取消方法。
// 可以選擇在取消時(shí)設(shè)置 resumeData 屬性
cancel(producingResumeData shouldProduceResumeData: Bool)
// 將生成的恢復(fù)數(shù)據(jù)提供給傳遞進(jìn)來(lái)的閉包
cancel(byProducingResumeData completionHandler: @escaping (_ data: Data?) -> Void)
d拆讯、請(qǐng)求狀態(tài)
盡管 Request
不封裝任何特定類(lèi)型的請(qǐng)求脂男,但它包含 Alamofire
執(zhí)行的所有請(qǐng)求所共有的狀態(tài)和功能,表示 Request
生命周期中的主要事件种呐。請(qǐng)求在創(chuàng)建后以 .initialized
狀態(tài)啟動(dòng)宰翅。通過(guò)調(diào)用適當(dāng)?shù)纳芷诜椒ǎ梢話炱鹚摇⒒謴?fù)和取消 Request
汁讼。
public enum State
{
case initialized
case resumed
case suspended
case cancelled
case finished
}
-
resume(): 恢復(fù)或啟動(dòng)請(qǐng)求的網(wǎng)絡(luò)流量。如果
startRequestsImmediately
為true
,則在將響應(yīng)handlers
添加到Request
后自動(dòng)調(diào)用此函數(shù)嘿架。 -
suspend(): 掛起或暫停請(qǐng)求及其網(wǎng)絡(luò)流量瓶珊。此狀態(tài)下的
Request
可以繼續(xù),但只有DownloadRequest
才能繼續(xù)傳輸數(shù)據(jù)眶明。其他Request
將重新開(kāi)始艰毒。 -
cancel(): 取消請(qǐng)求。一旦進(jìn)入此狀態(tài)搜囱,就無(wú)法恢復(fù)或掛起
Request
丑瞧。調(diào)用cancel()
時(shí),將使用AFError.explicitlyCancelled
實(shí)例設(shè)置請(qǐng)求的error
屬性蜀肘。 -
finished(): 如果一個(gè)
Request
被恢復(fù)并且在以后沒(méi)有被取消绊汹,那么它將在所有響應(yīng)驗(yàn)證器和響應(yīng)序列化器運(yùn)行之后到達(dá).finished
狀態(tài)。但是扮宠,如果在請(qǐng)求達(dá)到.finished
狀態(tài)后將其他響應(yīng)序列化器添加到該請(qǐng)求西乖,則它將轉(zhuǎn)換回.resumed
狀態(tài)并再次執(zhí)行網(wǎng)絡(luò)請(qǐng)求。
e坛增、請(qǐng)求進(jìn)度
為了跟蹤請(qǐng)求的進(jìn)度获雕,Request
提供了 uploadProgress
和 downloadProgress
屬性以及基于閉包的 uploadProgress
和 downloadProgress
方法。與所有基于閉包的 Request APIs
一樣收捣,進(jìn)度 APIs
可以與其他方法鏈接到 Request
之后届案,但是應(yīng)該在添加任何響應(yīng) handlers
(如 responseJSON
)之前添加到請(qǐng)求中。但并不是所有的 Request
子類(lèi)都能夠準(zhǔn)確地報(bào)告它們的進(jìn)度罢艾,有些可能有其他依賴項(xiàng)來(lái)報(bào)告它們的進(jìn)度楣颠。對(duì)于下載進(jìn)度,只有一個(gè)要求咐蚯,服務(wù)器響應(yīng)必須包含 Content-Length header
童漩。
AF.request("https://httpbin.org/get")
.uploadProgress
{ progress in
print("上傳進(jìn)度: \(progress.fractionCompleted)")
}
.downloadProgress
{ progress in
print("下載進(jìn)度: \(progress.fractionCompleted)")
}
.responseJSON
{ response in
debugPrint(response)
}
輸出結(jié)果為:
下載進(jìn)度: 1.0
[Result]: success({
args = {
};
headers = {
Accept = "*/*";
"Accept-Encoding" = "br;q=1.0, gzip;q=0.9, deflate;q=0.8";
"Accept-Language" = "en;q=1.0";
Host = "httpbin.org";
"User-Agent" = "UseAlamofire/1.0 (com.xiejiapei.UseAlamofire; build:1; iOS 14.3.0) Alamofire/5.4.1";
"X-Amzn-Trace-Id" = "Root=1-600f8c8f-1550b7515a19e35b7f534f0e";
};
origin = "218.104.139.115";
url = "https://httpbin.org/get";
})
對(duì)于上傳進(jìn)度,可以通過(guò)以下方式確定進(jìn)度
- 通過(guò)作為上傳
body
提供給UploadRequest
的Data
對(duì)象的長(zhǎng)度春锋。 - 通過(guò)作為
UploadRequest
的上傳body
提供的磁盤(pán)上文件的長(zhǎng)度矫膨。 - 通過(guò)根據(jù)請(qǐng)求的
Content-Length header
的值(如果已手動(dòng)設(shè)置)。
f看疙、調(diào)整和重試請(qǐng)求
重定向
Alamofire
的 RedirectHandler
協(xié)議提供了對(duì) Request
的重定向處理的控制和定制豆拨。除了每個(gè) Session
RedirectHandler
之外,每個(gè) Request
都可以被賦予屬于自己的 RedirectHandler
能庆,并且這個(gè) handler
將重寫(xiě) Session
提供的任何 RedirectHandler
施禾。
let redirector = Redirector(behavior: .follow)
AF.request("https://httpbin.org/get")
.redirect(using: redirector)
.responseDecodable(of: String.self)
{ response in
debugPrint(response)
}
重試
Alamofire
的 RequestInterceptor
協(xié)議由 RequestAdapter
和 RequestRetrier
協(xié)議組成。在身份驗(yàn)證系統(tǒng)中搁胆,向每個(gè) Request
添加一個(gè)常用的 headers
弥搞,并在授權(quán)過(guò)期時(shí)重試 Request
邮绿。Alamofire
還包含一個(gè)內(nèi)置的 RetryPolicy
類(lèi)型,當(dāng)由于各種常見(jiàn)的網(wǎng)絡(luò)錯(cuò)誤而導(dǎo)致請(qǐng)求失敗時(shí)攀例,可以輕松重試船逮。
RequestAdapter
協(xié)議允許在通過(guò)網(wǎng)絡(luò)發(fā)出之前檢查和修改 Session
執(zhí)行的每個(gè) URLRequest
。適配器的一個(gè)常見(jiàn)的用途粤铭,是在身份驗(yàn)證后面為請(qǐng)求添加 Authorization header
挖胃。RequestRetrier
協(xié)議允許重試在執(zhí)行時(shí)遇到錯(cuò)誤的請(qǐng)求。
3梆惯、Security
在與服務(wù)器和 web
服務(wù)通信時(shí)使用安全的 HTTPS
連接是保護(hù)敏感數(shù)據(jù)的重要步驟酱鸭。默認(rèn)情況下,Alamofire
接收與 URLSession
相同的自動(dòng) TLS
證書(shū)和證書(shū)鏈驗(yàn)證垛吗。雖然這保證了證書(shū)鏈的有效性凹髓,但并不能防止中間人(MITM
)攻擊或其他潛在的漏洞。為了減輕 MITM 攻擊怯屉,處理敏感客戶數(shù)據(jù)或財(cái)務(wù)信息的應(yīng)用程序應(yīng)使用 Alamofire
的 ServerTrustEvaluating
協(xié)議提供的證書(shū)或公鑰固定蔚舀。
a、評(píng)估服務(wù)器信任
ServerTrustEvaluting 協(xié)議
- DefaultTrustEvaluator:使用默認(rèn)服務(wù)器信任評(píng)估锨络,同時(shí)允許您控制是否驗(yàn)證質(zhì)詢提供的主機(jī)赌躺。
- RevocationTrustEvaluator:檢查接收到的證書(shū)的狀態(tài)以確保它沒(méi)有被吊銷(xiāo)。這通常不會(huì)在每個(gè)請(qǐng)求上執(zhí)行羡儿,因?yàn)樗枰W(wǎng)絡(luò)請(qǐng)求開(kāi)銷(xiāo)寿谴。
- PinnedCertificatesTrustEvaluator:使用提供的證書(shū)驗(yàn)證服務(wù)器信任。如果某個(gè)固定證書(shū)與某個(gè)服務(wù)器證書(shū)匹配失受,則認(rèn)為服務(wù)器信任有效。此評(píng)估器還可以接受自簽名證書(shū)咏瑟。
- PublicKeysTrustEvaluator:使用提供的公鑰驗(yàn)證服務(wù)器信任拂到。如果某個(gè)固定公鑰與某個(gè)服務(wù)器證書(shū)公鑰匹配,則認(rèn)為服務(wù)器信任有效码泞。
-
CompositeTrustEvaluator:評(píng)估一個(gè)
ServerTrustEvaluating
值數(shù)組兄旬,只有在所有數(shù)組中值都成功時(shí)才成功。此類(lèi)型可用于組合余寥,例如领铐,RevocationTrustEvaluator
和PinnedCertificatesTrustEvaluator
。 - DisabledEvaluator:此評(píng)估器應(yīng)僅在調(diào)試方案中使用宋舷,因?yàn)樗盟星笾敌髂欤@些求值又將始終認(rèn)為任何服務(wù)器信任都是有效的。此評(píng)估器不應(yīng)在生產(chǎn)環(huán)境中使用祝蝠!
// 此方法提供從底層 URLSession 接收的 SecTrust 值和主機(jī) String音诈,并提供執(zhí)行各種評(píng)估的機(jī)會(huì)
func evaluate(_ trust: SecTrust, forHost host: String) throws
ServerTrustManager
- cert.example.com:將始終在啟用默認(rèn)和主機(jī)驗(yàn)證的情況下使用證書(shū)固定
- keys.example.com:將始終在啟用默認(rèn)和主機(jī)驗(yàn)證的情況下使用公鑰固定
let evaluators: [String: ServerTrustEvaluating] =
[
// 默認(rèn)情況下幻碱,包含在 app bundle 的證書(shū)會(huì)自動(dòng)固定。
"cert.example.com": PinnedCertificatesTrustEvalutor(),
// 默認(rèn)情況下细溅,包含在 app bundle 的來(lái)自證書(shū)的公鑰會(huì)被自動(dòng)使用褥傍。
"keys.example.com": PublicKeysTrustEvalutor(),
]
let manager = ServerTrustManager(evaluators: serverTrustPolicies)
b、Logging
EventMonitor
協(xié)議的最大用途是實(shí)現(xiàn)相關(guān)事件的日志記錄喇聊。
final class Logger: EventMonitor
{
let queue = DispatchQueue(label: "xiejiapei")
// Event called when any type of Request is resumed.
func requestDidResume(_ request: Request)
{
print("Resuming: \(request)")
}
// Event called whenever a DataRequest has parsed a response.
func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>)
{
debugPrint("Finished: \(response)")
}
}
let logger = Logger()
let session = Session(eventMonitors: [logger])
session.request("https://httpbin.org/get").responseJSON
{ response in
debugPrint(response)
}
輸出結(jié)果為:
[Request]: GET https://httpbin.org/get
[Headers]: None
[Body]: None
[Response]: None
[Network Duration]: None
[Serialization Duration]: 4.879705375060439e-05s
[Result]: failure(Alamofire.AFError.sessionDeinitialized)
Resuming: GET https://httpbin.org/get
"Finished: failure(Alamofire.AFError.sessionDeinitialized)"
Demo
Demo在我的Github上恍风,歡迎下載。
UseFrameworkDemo