Alamofire - 如何从 AFError 中获取 API 错误

Alamofire - How to get API error from AFError

在我正确实施 Alamofire 5 和处理自定义错误模型响应的过程中,我还没有找到有示例的可接受答案。

为了尽可能彻底,这是我的apiclient

class APIClient {
    
    static let sessionManager: Session = {
        let configuration = URLSessionConfiguration.af.default
        
        configuration.timeoutIntervalForRequest = 30
        configuration.waitsForConnectivity = true
        
        return Session(configuration: configuration, eventMonitors: [APILogger()])
    }()
    
    @discardableResult
    private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:@escaping (Result<T, AFError>)->Void) -> DataRequest {
            return sessionManager.request(route)
//                .validate(statusCode: 200..<300) // This will kill the server side error response...
                .responseDecodable (decoder: decoder){ (response: DataResponse<T, AFError>) in
                    completion(response.result)
                }
        }
    
    static func login(username: String, password: String, completion:@escaping (Result<User, AFError>)->Void) {
        performRequest(route: APIRouter.login(username: username, password: password), completion: completion)
    }
}

我是这样用的

APIClient.login(username: "", password: "") { result in
    debugPrint(result)
    switch result {
    case .success(let user):
        debugPrint("__________SUCCESS__________")
    case .failure(let error):
        debugPrint("__________FAILURE__________")
        debugPrint(error.localizedDescription)
    }
}

我注意到,如果我使用 .validate(),调用函数将收到一个失败消息,但响应数据丢失。环顾四周,注意到 and here 施放 underlyingError 但那是零。

服务器以我在调用函数级别需要的可解析错误模型进行响应。在apiclient级别反序列化JSON并return它作为失败返回到调用函数会更令人愉快。

{
    "errorObject": {
        "summary": "",
        "details": [{
            ...
        }]
    }
}

更新

感谢@GIJoeCodes 的评论,我使用路由器实现了这个类似的解决方案。

class APIClient {
    
    static let sessionManager: Session = {
        let configuration = URLSessionConfiguration.af.default
        
        configuration.timeoutIntervalForRequest = 30
        configuration.waitsForConnectivity = true
        
        return Session(configuration: configuration, eventMonitors: [APILogger()])
    }()
    
    @discardableResult
    private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:@escaping (_ response: T?, _ error: Error?)->Void) {
        
        sessionManager.request(route)
            .validate(statusCode: 200..<300) // This will kill the server side error response...
            .validate(contentType: ["application/json"])
            .responseJSON { response in
                
                guard let data = response.data else { return }
                do {
                    switch response.result {
                    case .success:
                        let object = try decoder.decode(T.self, from: data)
                        completion(object, nil)
                        
                    case .failure:
                        let error = try decoder.decode(ErrorWrapper.self, from: data)
                        completion(nil, error.error)
                        
                    }
                } catch {
                    debugPrint(error)
                }
            }
        }
    
    // MARK: - Authentication
    static func login(username: String, password: String, completion:@escaping (_ response: User?, _ error: Error?)->Void) {
        performRequest(route: APIRouter.login(username: username, password: password), completion: completion)
    }
}

这样调用

APIClient.login(username: "", password: "") { (user, error) in
    if let error = error {
        debugPrint("__________FAILURE__________")
        debugPrint(error)
        return
    }
    
    if let user = user {
        debugPrint("__________SUCCESS__________")
        debugPrint(user)
    }
}

这就是我获取错误和自定义错误消息的方式。在验证中,我得到了 200..<300 响应之外的错误:

    AF.request(
        url,
        method: .post,
        parameters: json,
        encoder: JSONParameterEncoder.prettyPrinted,
        headers: headers
    ).validate(statusCode: 200..<300)
    .validate(contentType: ["application/json"])
    .responseJSON { response in

        switch response.result {
        case .success(let result):
            let json = JSON(result)
            
            onSuccess()
            
        case .failure(let error):
            
            guard let data = response.data else { return }

            do {
                let json = try JSON(data: data)
                
                let message = json["message"]
                onError(message.rawValue as! String)

            } catch {
                print(error)
            }
            
            onError(error.localizedDescription)
        }
        
        debugPrint(response)
    }

首先,如果您已经拥有 Decodable 模型,则无需使用 responseJSON。您通过多次解码响应数据来做不必要的工作。使用 responseDecodable 并提供您的 Decodable 类型,在本例中为您的通用 TresponseDecodable(of: T).

其次,将您期望的 Decodable 类型包装在枚举中是解决此问题的典型方法。例如:

enum APIResponse<T: Decodable> {
  case success(T)
  case failure(APIError)
}

然后实现 APIResponseDecodable 来尝试解析成功的类型或 APIError (有很多这样的例子)。然后,您可以使用 responseDecodable(of: APIResponse<T>.self).

解析您的响应