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)])
    
  • 相关阅读:
    (转载)C++ string中find() ,rfind() 等函数 用法总结及示例
    UVA 230 Borrowers (STL 行读入的处理 重载小于号)
    UVA 12100 打印队列(STL deque)
    uva 12096 The SetStack Computer(STL set的各种库函数 交集 并集 插入迭代器)
    uva 1592 Database (STL)
    HDU 1087 Super Jumping! Jumping! Jumping!
    hdu 1176 免费馅饼
    HDU 1003 Max Sum
    转战HDU
    hust 1227 Join Together
  • 原文地址:https://www.cnblogs.com/taoweiji/p/11538178.html
Copyright © 2011-2022 走看看