Alamofire - 如何从 AFError 获取 API 错误

Posted

技术标签:

【中文标题】Alamofire - 如何从 AFError 获取 API 错误【英文标题】:Alamofire - How to get API error from AFError 【发布时间】:2021-09-22 00:28:10 【问题描述】:

在我寻求正确实施 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() 调用函数将收到失败但响应数据丢失。环顾四周,注意到 here 和 here 转换为 underlyingError 但那是零。

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


    "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)
    

【问题讨论】:

【参考方案1】:

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

    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)
    

【讨论】:

这可能很好用,但是我正在寻找一个包含路由器的解决方案。我对 swift 和 alamofire 很陌生,似乎基于 .responsetype 的闭包很挑剔,即 responseDecodable、responseJSON 等。尝试在上述结构中切换它,我收到“来自抛出函数的无效转换”。使用 xcode 和 swift 一个多星期,我可能不明白这很简单。【参考方案2】:

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

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

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

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

【讨论】:

这是我第一次看到 APIResponse 枚举,我花了几天时间寻找解决方案。请提供指向其使用的完整示例的链接。这将是一个更清洁的解决方案。 我能够找到一个文档,其中包含上述枚举的详细信息。 swiftbysundell.com/articles/the-power-of-result-types-in-swift我不明白您所说的“然后实现 APIResponse 的 Decodable 以尝试解析成功的类型或 APIError”是什么意思。看这个文档,有一个解码块,很明显我不明白该怎么做,因为当按照示例实现时,会有很多错误。

以上是关于Alamofire - 如何从 AFError 获取 API 错误的主要内容,如果未能解决你的问题,请参考以下文章

使用消息“Alamofire.AFError.ResponseValidationFailureReason.unacceptableContentType”从存储中获取图像时出错

使用 Flickr API 时出现 Alamofire AFError

Alamofire.AFError.responseValidationFailed

解析 JSON 时出现 AFError Alamofire 5

Alamofire:responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.inputData

值:(failure(Alamofire.AFError.explicitlyCancelled)) 使用 Alamofire 发布者时