Alamofire之Request基本流程
首先来看以下两段代码:
Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(" request == \(response.request as Any)") // 原始的URL请求 https://httpbin.org/get
print("response = \(response.response as Any)") // HTTP URL响应 一些请求头信息
print("data = \(response.data as Any)") // 服务器返回的数据 376 bytes 376字节
print("result = \(response.result as Any)") // 响应序列化结果,在这个闭包里,存储的是JSON数据
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
SessionManager.default.request("https://httpbin.org/get").responseJSON { response in
print(" request == \(response.request as Any)") // 原始的URL请求 https://httpbin.org/get
print("response = \(response.response as Any)") // HTTP URL响应 一些请求头信息
print("data = \(response.data as Any)") // 服务器返回的数据 376 bytes 376字节
print("result = \(response.result as Any)") // 响应序列化结果,在这个闭包里,存储的是JSON数据
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
其实上面的两种写法是一样的,现在就第一种来分析下Alamofire进行网络请求的基本流程:
1.SessionManager.default.request
public func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
return SessionManager.default.request(
url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
进行了很多的默认值的设置,这也是为什么我们可以传一个url直接请求的原因,SessionManager.default如下:
public static let `default`: SessionManager = {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
}()
调用了SessionManager(configuration: configuration) 初始化方法; 初始化具体干了什么鬼操作呢?
public init(
configuration: URLSessionConfiguration = URLSessionConfiguration.default,
delegate: SessionDelegate = SessionDelegate(),
serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
{
self.delegate = delegate
self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
}
- SessionManager类中创建了一个
SessionDelegate类
,用于处理所有的代理事件,完成了代理的移交
;
- SessionManager类中创建了一个
public let delegate: SessionDelegate
- 通过
self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
创建URLSession
- 通过
下面是commonInit的方法实现:
private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
session.serverTrustPolicyManager = serverTrustPolicyManager
delegate.sessionManager = self
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
guard let strongSelf = self else { return }
DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
}
}
delegate.sessionManager = self , SessionManager类中创建了一个SessionDelegate类.SessionDelegate类里面传入了SessionManager,形成一个回环,然后通过weak打破循环引用.
delegate.sessionDidFinishEventsForBackgroundURLSession这个是后台下载完成的回调,这个前面章节介绍过了.
初始化过程总结一下就是:
- URLSession的配置
- 代理的移交,
- 创建了一个后台下载完成的回调闭包
2. request
open func request(
_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil)
-> DataRequest
{
var originalRequest: URLRequest?
do {
originalRequest = try URLRequest(url: url, method: method, headers: headers)
let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
return request(encodedURLRequest)
} catch {
return request(originalRequest, failedWith: error)
}
}
我们就传了一个URL URLConvertible = https://httpbin.org/get
其他的使用默认值(swift的可以这样干😑);
创建URLRequest
并进行相应的信息配置:
originalRequest = try URLRequest(url: url, method: method, headers: headers)
通过url,method,headers
创建URLRequest
(通过url创建request,用request创建task);
let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
参数处理,Parameters
是要传递什么参数,比如登录的username,password:
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
guard let parameters = parameters else { return urlRequest }
if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
guard let url = urlRequest.url else {
throw AFError.parameterEncodingFailed(reason: .missingURL)
}
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
urlComponents.percentEncodedQuery = percentEncodedQuery
urlRequest.url = urlComponents.url
}
} else {
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
}
return urlRequest
}
通过ifelse知道主要把请求分成两种情况处理,进入到if中的可以 是.get, .head, .delete
,其他为else方法实现:
private func encodesParametersInURL(with method: HTTPMethod) -> Bool {
…
switch method {
case .get, .head, .delete:
return true
default:
return false
}
}
得到完整的请求percentEncodedQuery
赋值给urlComponents.percentEncodedQuery
,URLComponents
是一个结构体,可以保存信息;
下面对着get,post两种情况进行分析:
1.get
let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
分析:
-
(urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "")
为路由的处理; -
query(parameters)
为传入的参数的处理;对参数进行百分号编码处理,其中query(parameters)
主要是对传递的参数进行递归处理,拼接
:
private func query(_ parameters: [String: Any]) -> String {
var components: [(String, String)] = []
//以下为分情况递归遍历,拿到传入的所有的数据🍎
for key in parameters.keys.sorted(by: <) {
let value = parameters[key]!
//key 和value进行组装,返回的数组里面的放的是元祖🍎
components += queryComponents(fromKey: key, value: value)
}
//例: username=XXXX&passward=OOOO XXXX,OOOO 为百分号编码结果🍎
return components.map { "\($0)=\($1)" }.joined(separator: "&")
}
//递归处理🍎
public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
var components: [(String, String)] = []
if let dictionary = value as? [String: Any] {
for (nestedKey, value) in dictionary {
components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
}
} else if let array = value as? [Any] {
for value in array {
components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value)
}
} else if let value = value as? NSNumber {
if value.isBool {
components.append((escape(key), escape(boolEncoding.encode(value: value.boolValue))))
} else {
components.append((escape(key), escape("\(value)")))
}
} else if let bool = value as? Bool {
components.append((escape(key), escape(boolEncoding.encode(value: bool))))
} else {
components.append((escape(key), escape("\(value)")))
}
return components
}
2.post
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
可以看到,post是把学习存储在了httpBody,同时数据是进行了处理的;一般我们传递给服务器时,后台也会要求我们把数据转成json发送,比如下面这样:
extension ViewController {
// options: .prettyPrinted 使用空白和缩进使输出更具可读性的写入选项
fileprivate func getJsonFromArray(_ array:Array<Any>) -> String{
let data = try? JSONSerialization.data(withJSONObject: array, options: .prettyPrinted)
return String(data: data!, encoding: .utf8) ?? ""
}
}
post其他具体的就不再深究了.
数据处理完成后调用request(encodedURLRequest)
,传入处理好的数据:
open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
var originalRequest: URLRequest?
do {
//取出之前配置的URLRequest;🍎
originalRequest = try urlRequest.asURLRequest()
//设置类的结构体中的成员urlRequest的值,其实就是保存了URLRequest🍎
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
////创建task任务🍎
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
delegate[task] = request
if startRequestsImmediately { request.resume() }
return request
} catch {
return request(originalRequest, failedWith: error)
}
}
说明:
let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
;设置类的结构体中的成员urlRequest
的值,其实就是默认初始化一个结构体,并设置结构体中的属性urlRequest
的值,也就是保存了URLRequest;
所以可以理解为这里是一个储存型结构体.结构体的一种常规使用方法,值得我们学习;- 为什么要这样干?其实也可以直接
DataRequest
类中定义一个task
属性,通过DataRequest
去创建,但是这样的话,task
相关的所有操作都需要DataRequest
去处理,而且DataRequest
和task
不应该有一个包含关系,他们更像是同一级的,由DataRequest
去创建task
,会导致DataRequest
过重;- 而既然这样那为什么不定义一个task类去处理呢 ? 因为我们需要保存
urlRequest
的值;- 最后,我们的
DataRequest
和task
是有对应关系的,通过结构体把这个依赖打破了,降低耦合,那task
和DataRequest
的对应关系怎么办? 关联方法--->delegate[task] = request;
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
,调用结构体中的task
方法,创建task
任务,实现方法中调用了{ session.dataTask(with: urlRequest) }
同步创建task,
这里传入了一个adapt
,这个参数下一章节再介绍.
//这个是DataRequest中的结构体Requestable🍎
struct Requestable: TaskConvertible {
let urlRequest: URLRequest
func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
do {
let urlRequest = try self.urlRequest.adapt(using: adapter)
return queue.sync { session.dataTask(with: urlRequest) }
} catch {
throw AdaptError(error: error)
}
}
}
以上主要就是创建task任务,同时在结构体中保存了URLRequest
Request.swift
中的类其实有4种
,都是,Request
的子类,——根据不同需求会调用不同的类的方法;如下载时,我们调用的不再是DataRequest类
,而是DownloadRequest类
,同时所有的子类继承了Request
的方法,因为这些方法是每个子类所必须要有的功能,所以写在一起了这里.这种写法也是值得大家去参考的,主要是共有的功能的抽离形成父类.
let request = DataRequest(session: session, requestTask: .data(originalTask, task))
其实就是调用父类的初始化方法,这一句才是DataRequest
的初始化,所以很明显能感觉到,前面的task
的创建和DataRequest
是分离的.而创建的类型通过枚举requestTask
传参决定,这就是工厂设计模式
,同时这里也涉及到枚举
的常规所用,这些都是非常值得我们学习的地方.
open class Request {
…
enum RequestTask {
case data(TaskConvertible?, URLSessionTask?)
case download(TaskConvertible?, URLSessionTask?)
case upload(TaskConvertible?, URLSessionTask?)
case stream(TaskConvertible?, URLSessionTask?)
}
…
init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
self.session = session
//data传参决定创建类型,典型的工厂设计🍎
switch requestTask {
case .data(let originalTask, let task):
taskDelegate = DataTaskDelegate(task: task)
self.originalTask = originalTask
case .download(let originalTask, let task):
taskDelegate = DownloadTaskDelegate(task: task)
self.originalTask = originalTask
case .upload(let originalTask, let task):
taskDelegate = UploadTaskDelegate(task: task)
self.originalTask = originalTask
case .stream(let originalTask, let task):
taskDelegate = TaskDelegate(task: task)
self.originalTask = originalTask
}
delegate.error = error
delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}
…
}
DataRequest
的初始化 还创建了一个taskDelegate
,让self.originalTask = 创建的taskdelegate
,把两个delegate关联就在这个方法里面;let originalTask: TaskConvertible?
, self是Request类
,self.originalTask其实就是Request中的TaskConvertible对象
;
我们前面提到了一个代理的移交,回顾一下: URLSession的代理
———>移交给了SessionManager类
中的 SessionDelegate类
;当请求的数据返回时,通过系统的URLSession
的代理拿到数据;因为代理移交的关系,会响应SessionDelegate
类里面的方法:
open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
/// Executed after it is determined that the request is not going to be retried
//一个闭包🍎
let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
guard let strongSelf = self else { return }
//self是SessionDelegate类, 这里的意思是,对外的方法taskDidComplete用户是否实现?如果实现了,把数据传给外界.🍎
//实现方法就是这样:SessionManager.default.delegate.taskDidComplete= {…}🍎
strongSelf.taskDidComplete?(session, task, error)
//strongSelf[task]下标法去到Request,然后任务下发,而delegate就是taskDelegate(内部定义的属性),上面我们说到了,self.originalTask = 创建的taskdelegate,🍎
//也就意味着, .urlSession方法会调用TaskDelegate的urlSession方法🍎
strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)
//这下面是通知的响应,流程和上面是一样的,先不管🍎
var userInfo: [String: Any] = [Notification.Key.Task: task]
if let data = (strongSelf[task]?.delegate as? DataTaskDelegate)?.data {
userInfo[Notification.Key.ResponseData] = data
}
NotificationCenter.default.post(
name: Notification.Name.Task.DidComplete,
object: strongSelf,
userInfo: userInfo
)
strongSelf[task] = nil
}
//下面是一系列判断后对上面那个闭包的调用,所以下面的不看了,我们分析上面的闭包
...一大堆判断后调用
completeTask(session, task, error)
...
下发到具体的TaskDelegate的urlSession方法中:
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let taskDidCompleteWithError = taskDidCompleteWithError {
//是否自己实现了错误处理,是的话,传出去🍎
taskDidCompleteWithError(session, task, error)
} else {
if let error = error {
//是否有错误,有的话进行错误处理🍎
if self.error == nil { self.error = error }
if
let downloadDelegate = self as? DownloadTaskDelegate,
let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
{
downloadDelegate.resumeData = resumeData
}
}
//任务队列开启🍎
queue.isSuspended = false
}
}
总结一下就是:
- 创建
URLRequest
- 创建
task
任务,同时在结构体中保存了URLRequest
- 通过
URLSession
和task
创建一个Request
Request
进行resume
- 到此
.request
解析完成,返回了request
,形成链式请求.
未完待续,下一章节继续分析.