zoukankan      html  css  js  c++  java
  • 基于Moya、RxSwift和ObjectMapper优雅实现REST API请求

    在Android开发中有非常强大的 Retrofit 请求,结合RxJava可以非常方便实现 RESTful API 网络请求。在 iOS开发中也有非常强大的网络请求库 Moya ,Moya是一个基于 Alamofire 开发的,轻量级的Swift网络层。Moya的可扩展性非常强,可以方便和RXSwift、ObjectMapper结合。

    测试 REST API 定义

    我们先用服务端定义几个REST API,开发者根据自己的条件来实现。

    请求错误格式实例
    {
        "error": "密码错误",
        "error_code": "password_error"
    }
    
    测试 API 列表
    1. http://127.0.0.1:8080/account/login,参数username、password,post请求,成功响应为User。
    2. http://127.0.0.1:8080/user/{userId},get请求,成功响应为User。
    3. http://127.0.0.1:8080/user/query?q={keyword},get请求,成功响应为User列表。

    创建接口

    // MyApiService.swift
    import Moya
    
    enum MyApiService {
        case login(username:String,password:String)
        case user(userId:String)
        case userQuery(keyword:String)
    }
    extension MyApiService:TargetType{
        // 定义请求的host
        var baseURL: URL {
            return URL(string: "http://127.0.0.1:8080")!
        }
        // 定义请求的路径
        var path: String {
            switch self {
            case .login(_, _):
                return "/account/login"
            case .user(let userId):
                return "user/(userId)"
            case .userQuery(_):
                return "user/query"
            }
        }
        // 定义接口请求方式
        var method: Moya.Method {
            switch self {
            case .login:
                return .post
            case .user,.userQuery:
                return .get
            }
        }
        // 定义模拟数据
        var sampleData: Data {
            switch self {
            case .login(let username, _):
                return "{"username": "(username)", "id": 100}".data(using: String.Encoding.utf8)!
            case .user(_):
                return "{"username": "Wiki", "id": 100}".data(using: String.Encoding.utf8)!
            case .userQuery(_):
                return "{"username": "Wiki", "id": 100}".data(using: String.Encoding.utf8)!
            }
        }
        // 构建参数
        var task: Task {
            switch self {
            case .login(let username, let passowrd):
                return .requestParameters(parameters: ["username": username,"passowrd": passowrd], encoding: URLEncoding.default)
            case .user(_):
                return .requestPlain
            case .userQuery(let keyword):
                return .requestParameters(parameters: ["keyword": keyword], encoding: URLEncoding.default)
            }
        }
        // 构建请求头部
        var headers: [String : String]? {
            return ["Content-type": "application/json"]
        }
    }
    

    请求数据

    let provider = MoyaProvider<MyApiService>()
    
    // Moya 提供最原始的请求方式,响应的数据是二进制
    provider.request(.user(userId: "101")){ result in
            // do something with the result
            let text = String(bytes: result.value!.data, encoding: .utf8)
        print("text1 = (text)")
    }
    
    // 结合RxSwift,响应的数据是二进制
    provider.rx.request(.user(userId: "101")).subscribe({result in
            // do something with the result
            switch result {
            case let .success(response):
                let text = String(bytes: response.data, encoding: .utf8)
                print("text2 = (text)")
            case let .error(error):
                print(error)
        }
    })
    
    // 通过mapJSON把数据转换成json格式
    provider.rx.request(.user(userId: "101")).mapJSON().subscribe({result in
            // do something with the result
            switch result {
            case let .success(text):
                print("text3 = (text)")
            case let .error(error):
                print(error)
        }
    })
    // 通过mapJSON把数据转换成json格式,并转换成最常见的Observable
    provider.rx.request(.user(userId: "101")).mapJSON().asObservable().subscribe(onNext: { result in
            // do something with the result
            print("text4 = (result)")
    }, onError:{ error in
        // do something with the error
    })
    
    请求数据:RxBlocking

    RxBlocking使用教程 ,可以使用同步的方式请求网络

    import RxBlocking
    
    do{
        let text = try provider.rx.request(.user(userId: "101")).mapJSON().toBlocking().first()
        print("text5 = (text)")
    }catch{
        print(error)
    }
    

    结合 ObjectMapper

    引入ObjectMapper
    pod 'ObjectMapper', '~> 3.4'
    
    编写RxSwift拓展代码
    //  MoyaRxSwiftObjectMapperExtension.swift
    
    import Foundation
    import RxSwift
    import Moya
    import ObjectMapper
    
    public extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
        func mapObject<T: BaseMappable>(type: T.Type) -> Single<T> {
            return self.map{ response in
                return try response.mapObject(type: type)
            }
        }
        func mapArray<T: BaseMappable>(type: T.Type) -> Single<[T]> {
            return self.map{ response in
                return try response.mapArray(type: type)
            }
        }
    }
    public extension ObservableType where E == Response {
        func mapObject<T: BaseMappable>(type: T.Type) -> Observable<T> {
            return self.map{ response in
                return try response.mapObject(type: type)
            }
        }
        func mapArray<T: BaseMappable>(type: T.Type) -> Observable<[T]> {
            return self.map{ response in
                return try response.mapArray(type: type)
            }
        }
    }
    
    public extension Response{
        func mapObject<T: BaseMappable>(type: T.Type) throws -> T{
            let text = String(bytes: self.data, encoding: .utf8)
            if self.statusCode < 400 {
                return Mapper<T>().map(JSONString: text!)!
            }
            do{
                let serviceError = Mapper<ServiceError>().map(JSONString: text!)
                throw serviceError!
            }catch{
                if error is ServiceError {
                    throw error
                }
                let serviceError = ServiceError()
                serviceError.message = "服务器开小差,请稍后重试"
                serviceError.error_code = "parse_error"
                throw serviceError
            }
        }
        func mapArray<T: BaseMappable>(type: T.Type) throws -> [T]{
            let text = String(bytes: self.data, encoding: .utf8)
            if self.statusCode < 400 {
                return Mapper<T>().mapArray(JSONString: text!)!
            }
            do{
                let serviceError = Mapper<ServiceError>().map(JSONString: text!)
                throw serviceError!
            }catch{
                if error is ServiceError {
                    throw error
                }
                let serviceError = ServiceError()
                serviceError.message = "服务器开小差,请稍后重试"
                serviceError.error_code = "parse_error"
                throw serviceError
            }
        }
    }
    class ServiceError:Error,Mappable{
        var message:String = ""
        var error_code:String = ""
        required init?(map: Map) {}
        init() {
            
        }
        func mapping(map: Map) {
            error_code <- map["error_code"]
            message <- map["error"]
        }
        var localizedDescription: String{
            return message
        }
    }
    
    
    创建 User 类
    //  User.swift
    import ObjectMapper
    class User: Mappable {
        required init?(map: Map) {}
        
        func mapping(map: Map) {
            userId <- map["userId"]
            name <- map["name"]
            age <- map["age"]
        }
        
        var userId:Int = 0
        var name:String = ""
        var age:Int = 0
    }
    
    测试
    do{
        let user = try provider.rx.request(.user(userId: "101")).mapObject(type: User.self).toBlocking().first()
        print("user.name = (user?.name)")
    }catch{
        print(error)
    }
    do{
        let user = try provider.rx.request(.user(userId: "101")).asObservable().mapObject(type: User.self).toBlocking().first()
        print("user.name = (user?.name)")
    }catch{
        print(error)
    }
    
    do{
        let users = try provider.rx.request(.userQuery(keyword: "Wiki")).mapArray(type: User.self).toBlocking().first()
        print("test8 users.count = (users?.count)")
    }catch{
        if error is ServiceError {
            print((error as! ServiceError).message)
        }
        print(error)
    }
    

    打印日志

    private func JSONResponseDataFormatter(_ data: Data) -> Data {
        do {
            let dataAsJSON = try JSONSerialization.jsonObject(with: data)
            let prettyData =  try JSONSerialization.data(withJSONObject: dataAsJSON, options: .prettyPrinted)
            return prettyData
        } catch {
            return data // fallback to original data if it can't be serialized.
        }
    }
    
    let provider = MoyaProvider<MyApiService>(plugins: [NetworkLoggerPlugin(verbose: true, responseDataFormatter: JSONResponseDataFormatter)])
    
  • 相关阅读:
    洛谷 P1278 单词游戏 【状压dp】
    洛谷 P1854 花店橱窗布置 【dp】
    洛谷 P2258 子矩阵
    洛谷 P3102 [USACO14FEB]秘密代码Secret Code 【区间dp】
    洛谷U14200 Changing 题解 【杨辉三角】
    洛谷P3933 Chtholly Nota Seniorious 【二分 + 贪心 + 矩阵旋转】
    P3932 浮游大陆的68号岛 【线段树】
    洛谷P1273 有线电视网 【树上分组背包】
    NOI2013 矩阵游戏 【数论】
    洛谷P1268 树的重量 【构造 + 枚举】
  • 原文地址:https://www.cnblogs.com/taoweiji/p/11538178.html
Copyright © 2011-2022 走看看