当前位置: > > > Swift - 网络抽象层库Moya的使用详解3(请求成功、失败的结果处理)

Swift - 网络抽象层库Moya的使用详解3(请求成功、失败的结果处理)

五、请求结果的处理

1,成功、失败响应的判断

(1)之前的样例中我们只处理数据成功返回的情况,其实 Moya 会将 Alamofire 成功或失败的响应包裹在 Result 枚举中返回,具体值如下:
  • .success(Moya.Response):成功的情况。我们可以从 Moya.Response 中得到返回数据(data)和状态(status
  • .failure(MoyaError):失败的情况。这里的失败指的是服务器没有收到请求(例如可达性/连接性错误)或者没有发送响应(例如请求超时)。我们可以在这里设置个延迟请求,过段时间重新发送请求。
DouBanProvider.request(.playlist(channelId)) { result in
    switch result {
    case let .success(response):
        let statusCode = response.statusCode // 响应状态码:200, 401, 500...
        let data = response.data // 响应数据
        //继续做一些其它事情....
    case let .failure(error):
        //错误处理....
        break
    }
}

(2)对于 .failure(error) 情况下,我们还可以通过 switch 语句判断具体的 MoyaError 错误类型:
case let .failure(error):
    switch error {
    case .imageMapping(let response):
        print("错误原因:\(error.errorDescription ?? "")")
        print(response)
    case .jsonMapping(let response):
        print("错误原因:\(error.errorDescription ?? "")")
        print(response)
    case .statusCode(let response):
        print("错误原因:\(error.errorDescription ?? "")")
        print(response)
    case .stringMapping(let response):
        print("错误原因:\(error.errorDescription ?? "")")
        print(response)
    case .underlying(let error, let response):
        print("错误原因:\(error.errorDescription ?? "")")
        print(error)
        print(response as Any)
    case .requestMapping:
        print("错误原因:\(error.errorDescription ?? "")")
        print("nil")
    }

比如当我们在没有网络的情况下发起请求,控制台输出如下(走的是 .underlying 分支):

2,过滤正确的状态码

除了连接超时这样的网络问题会返回 .failure,像是服务器报错(404)、请求未授权(401)等都是返回 .success 的。这样我们还需要根据状态码来判断返回数据是否是正确的。
Moya.Response 本身就提供了下面两个方法来过滤 response 响应:
  • filterSuccessfulStatusCodes():返回状态码为 200 - 299 的响应
  • filterSuccessfulStatusAndRedirectCodes():返回状态码为 200 - 399 的响应
DouBanProvider.request(.playlist(channelId)) { result in
    switch result {
    case let .success(response):
        do {
            //过滤成功的状态码响应
            try response.filterSuccessfulStatusCodes()
            let data = try response.mapJSON()
            //继续做一些其它事情....
        }
        catch {
            //处理错误状态码的响应...
        }
    case let .failure(error):
        //错误处理....
        break
    }
}

3,封装一个请求/结果处理的适配器

(1)虽然 Mayo 使用起来很方便,但如果每次都要对响应结果进行判断(连接是否成功、响应状态码是否正确)还是略显麻烦。我们可以自行定义一个适配器(adapter),将请求和结果的判断处理都封装起来。然后通过三个回调函数返回相应的结果,这三个回调函数分别对应三种响应情况:
  • success:服务器连接成功,且数据正确返回(同时会自动将数据转换成 JSON 对象,方便使用)
  • error:服务器连接成功,但数据返回错误(同时会返回错误信息)
  • failure :服务器连接不上,网络异常等(同时会返回错误信息。必要的话,还可以在此增加自动重新请求的机制。)
import Moya

struct Network {
    static let provider = MoyaProvider<DouBan>()
    
    static func request(
        _ target: DouBan,
        success successCallback: @escaping (JSON) -> Void,
        error errorCallback: @escaping (Int) -> Void,
        failure failureCallback: @escaping (MoyaError) -> Void
        ) {
        provider.request(target) { result in
            switch result {
            case let .success(response):
                do {
                    //如果数据返回成功则直接将结果转为JSON
                    try response.filterSuccessfulStatusCodes()
                    let json = try JSON(response.mapJSON())
                    successCallback(json)
                }
                catch let error {
                    //如果数据获取失败,则返回错误状态码
                    errorCallback((error as! MoyaError).response!.statusCode)
                }
            case let .failure(error):
                //如果连接异常,则返沪错误信息(必要时还可以将尝试重新发起请求)
                //if target.shouldRetry {
                //    retryWhenReachable(target, successCallback, errorCallback,
                //      failureCallback)
                //}
                //else {
                    failureCallback(error)
                //}
            }
        }
    }
}

(2)这样我们直接使用这个 adapter 请求数据,代码会清爽许多。
Network.request(.playlist(channelId), success: { json in
    //获取歌曲信息
    let music = json["song"].arrayValue[0]
    let artist = music["artist"].stringValue
    let title = music["title"].stringValue
    let message = "歌手:\(artist)\n歌曲:\(title)"
    //将歌曲信息弹出显示
    self.showAlert(title: channelName, message: message)
}, error: { statusCode in
    //服务器报错等问题
    print("请求错误!错误码:\(statusCode)")
}, failure: { error in
    //没有网络等问题
    print("请求失败!错误信息:\(error.errorDescription ?? "")")
})

(3)运行结果如下:
  • 当数据请求成功时,将返回结果弹出显示:

  • 当数据请求错误时,打印出错误的状态码:

  • 当连接失败时,打印出错误信息:
评论2