如何处理来自API的自定义错误消息?

时间:2020-06-06 16:51:57

标签: ios swift

我想处理来自API的错误消息以显示为警报。

例如,当用户尝试将游戏添加到愿望清单中但之前已经添加时,API会返回此错误消息以及417状态代码:

{
  "hasError" : true,
  "errorMessage" : [
    "Game is already in your wish list."
  ]
}

我想从'case .failure'中获取此错误消息,以在应用程序中显示为Alert。通常,它像NSError一样进行转换,但我想像以下模型一样处理它:

struct ErrorMessage: Codable {
    var errorMessage: [String]
    var hasError: Bool
}

我有一个HTTPManager:

class HttpManager {
    static let shared = HttpManager()

    private init() { }

    enum HttpError: Error {
        case invalidResponse(Data?, URLResponse?)
    }

    public func get(_ url: URL, token: String?, completionBlock: @escaping (Result<Data, Error>) -> Void) {

        var request = URLRequest(url: url)
        request.httpMethod = "GET"

        request.setValue("iOS", forHTTPHeaderField: "User-Agent")

        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard error == nil else {
                completionBlock(.failure(error!))
                return
            }

            guard
                let responseData = data,
                let httpResponse = response as? HTTPURLResponse,
                200 ..< 300 ~= httpResponse.statusCode else {
                    completionBlock(.failure(HttpError.invalidResponse(data, response)))
                    return
            }

            completionBlock(.success(responseData))
        }
        task.resume()
    }

    public func post(_ url: URL, parameters: [String:Any]?, completionBlock: @escaping (Result<Data, Error>) -> Void) {

        var request = URLRequest(url: url)
        request.httpMethod = "POST"


        request.setValue("iOS", forHTTPHeaderField: "User-Agent")

        if parameters != nil{
            guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters!, options: .prettyPrinted) else {
                return
            }
            request.httpBody = httpBody
        }

        //HTTP Headers
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")

        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard error == nil else {
                completionBlock(.failure(error!))
                return
            }

            guard
                let responseData = data,
                let httpResponse = response as? HTTPURLResponse,
                200 ..< 300 ~= httpResponse.statusCode else {
                    completionBlock(.failure(HttpError.invalidResponse(data, response)))
                    return
            }

            completionBlock(.success(responseData))
        }
        task.resume()
    }

}

我这样调用API:

func postWishList(game: Int, platform: Int, completion: @escaping (Result<BaseAddWishList, Error>) -> Void) {

        let url =  baseURL + postWishListURL

        let params =  ["gameId": game, "platform": platform] as [String : Any]

        HttpManager.shared.post(URL(string: url)!, parameters: params) { result in
            switch result {
            case .failure(let error):
                    DispatchQueue.main.async { completion(.failure(error)) }

            case .success(let data):
                do {
                    let res = try JSONDecoder().decode(BaseAddWishList.self, from: data)
                    DispatchQueue.main.async { completion(.success(res)) }
                } catch {
                    print("Unable to retrieve string representation")
                    DispatchQueue.main.async { completion(.failure(error)) }
                }
            }
        }
    }

还有我的ViewModel函数(我想在这里接收errorMessage以便在警报中显示)

func postWishList(game: Int, platform: Int){
        ApiManager.shared.postWishList(game: game, platform: platform) { [weak self] result in
            guard let self = self else { return }
            switch result {
            case .success(let result):
                print(result)
            case .failure(let err):
                print(err)
// I need errorMessage string at here to show as an Alert.
            }
        }
    }

1 个答案:

答案 0 :(得分:1)

如果您的服务器针对此类错误返回417,则必须删除200..<300状态代码检查。然后dataTask方法可以提供更精细的状态码检查:

public func post(_ url: URL, parameters: [String: Any]?, completion: @escaping (Result<Data, Error>) -> Void) {
    var request = URLRequest(url: url)
    request.httpMethod = "POST"

    request.httpBody = parameters.flatMap { try?JSONSerialization.data(withJSONObject: $0) }

    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard
            let responseData = data,
            let httpResponse = response as? HTTPURLResponse
        else {
            completion(.failure(HttpError.invalidResponse(data, response)))
            return
        }

        switch httpResponse.statusCode {
        case 200..<300:
            completion(.success(responseData))

        case 417:  // if there are others, add them to this list
            completion(.failure(HttpError.apiError(httpResponse.statusCode, responseData)))

        default:
            completion(.failure(HttpError.invalidStatusCode(httpResponse.statusCode)))
        }
    }
    task.resume()
}

哪里

enum HttpError: Error {
    case invalidResponse(Data?, URLResponse?)
    case invalidStatusCode(Int)
    case apiError(Int, Data)
}

然后在发布愿望时,您可以捕获以下状态代码并解析响应:

func postWishList(game: Int, platform: Int, completion: @escaping (Result<BaseAddWishList, Error>) -> Void) {
    let url =  baseURL + postWishListURL

    let params =  ["gameId": game, "platform": platform] as [String : Any]

    HttpManager.shared.post(URL(string: url)!, parameters: params) { result in
        switch result {
        case .failure(let postError):
            let error: Error

            switch postError {
            case HttpManager.HttpError.apiError(417, let data):
                do {
                    let res = try JSONDecoder().decode(ErrorMessage.self, from: data)
                    error = ApiError.wishFailure(res.errorMessage)
                } catch let parseError {
                    error = parseError
                }

            default:
                error = postError
            }

            DispatchQueue.main.async { completion(.failure(error)) }

        case .success(let data):
            do {
                let res = try JSONDecoder().decode(BaseAddWishList.self, from: data)
                DispatchQueue.main.async { completion(.success(res)) }
            } catch {
                print("Unable to retrieve string representation")
                DispatchQueue.main.async { completion(.failure(error)) }
            }
        }
    }
}

坦率地说,如果在整个API中都使用此ErrorMessage模式,我可能会将错误的分析移到HttpManager中,但是希望以上内容足以说明该模式。

相关问题