lection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] } extension ResponseCollectionSerializable where Self: ResponseObjectSerializable { static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] { var collection: [Self] = [] if let representation = representation as? [[String: Any]] { for itemRepresentation in representation { if let item = Self(response: response, representation: itemRepresentation) { collection.append(item) } } } return collection } }
extension DataRequest {
@discardableResult
func responseCollection<T: ResponseCollectionSerializable>(
queue: DispatchQueue? = nil,
completionHandler: @escaping (DataResponse<[T]>) -> Void) -> Self { let responseSerializer = DataResponseSerializer<[T]> { request, response, data, error in guard error == nil else { return .failure(BackendError.network(error: error!)) } let jsonSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) let result = jsonSerializer.serializeResponse(request, response, data, nil) guard case let .success(jsonObject) = result else { return .failure(BackendError.jsonSerialization(error: result.error!)) } guard let response = response else { let reason = "Response collection could not be serialized due to nil response." return .failure(BackendError.objectSerialization(reason: reason)) } return .success(T.collection(from: response, withRepresentation: jsonObject)) } return response(responseSerializer: responseSerializer, completionHandler: completionHandler) } }
struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible {
let username: String let name: String var description: String { return "User: { username: \(username), name: \(name) }" } init?(response: HTTPURLResponse, representation: Any) { guard let username = response.url?.lastPathComponent, let representation = representation as? [String: Any], let name = representation["name"] as? String else { return nil } self.username = username self.name = name } }
Alamofire.request("https://example.com/users").responseCollection { (response: DataResponse<[User]>) in debugPrint(response) if let users = response.result.value { users.forEach { print("- \($0)") } } }
安全
对于安全敏感的数据来说,在与服务器和web服务交互时使用安全的HTTPS连接是非常重要的一步。默认情况下,Alamofire会使用苹果安全框架内置的验证方法来评估服务器提供的证书链。虽然保证了证书链是有效的,但是不能防止man-in-the-middle (MITM)攻击或者其他潜在的漏洞。为了减少MITM攻击,处理用户的敏感数据或财务信息的应用,应该使用ServerTrustPolicy 提供的certificate或者public key pinning。
ServerTrustPolicy
在通过HTTPS安全连接连接到服务器时,ServerTrustPolicy 枚举通常会评估URLAuthenticationChallenge 提供的server trust。
let serverTrustPolicy = ServerTrustPolicy.pinCertificates( certificates: ServerTrustPolicy.certificates(), validateCertificateChain: true, validateHost: true )
在验证的过程中,有多种方法可以让我们完全控制server trust的评估:
performDefaulteva luation :使用默认的server trust评估,允许我们控制是否验证challenge提供的host。
pinCertificates :使用pinned certificates来验证server trust。如果pinned certificates匹配其中一个服务器证书,那么认为server trust是有效的。
pinPublicKeys :使用pinned public keys来验证server trust。如果pinned public keys匹配其中一个服务器证书公钥,那么认为server trust是有效的。
disableeva luation :禁用所有评估,总是认为server trust是有效的。
customeva luation :使用相关的闭包来评估server trust |