Alamofire+HandyJSON+泛型封裝的簡單離散式網絡框架

離散式與集約式網絡框架

集約式:

每個請求都會走統一的入口,對外暴露了請求的 URL 和 Param 以及請求方式,入口一般都是通過單例 來實現。例如

HttpClient.getInstance().sendRequest(url,params,callback)

優點:

1、使用便捷,可以快速開發

缺點:

1、對每個請求的定製要求不夠,例如,現在有個新需求,請求新聞條目需要保存網絡緩存,但其他網絡請求不需要。那麼我們必須新開一個sendRequest()方法,其中加一個參數isCache。這種情況對後期業務擴展非常不友好。

離散式:

即每個網絡請求類都是一個對象,它的 URL 以及請求方式和響應方式 均不暴露給外部調用。只能內部通過 重載或實現協議 的方式來指定,例如ios中的YTKNetWork即是這種方式。思想是爲每一個請求編寫一個配置類,在該類中重寫接口(協議)中定義的方法返回該網絡請求需要的參數。

優點:

1、擴展性強

2、URL 以及請求和響應方式不暴露給外部,避免外部調用的時候寫錯,設想一下,使用集約式設計,我們所有的URL地址均寫在一個類似URLContants的文件中,那麼新增、修改、刪除url都需要修改該文件,大大提高了出錯風險。

3、可定製性強,可以爲每個請求指定請求的超時時間以及緩存的週期

缺點:

每個網絡請求都要配置一個類,業務豐富後類數量較多

好,有了簡單的概念瞭解後,我們進入正題,設計一套離散式的網絡框架

 

swift離散式網絡框架

1、框架性代碼HttpRestfulClient

核心類,封裝第三方網絡請求庫,暴露請求方式,是單例。我就將代碼完全粘進來了,保證同學能看得懂

//
//  HttpRestfulClient.swift
//  HelloIOS
//
//  Created by zxg on 2018/10/29.
//  Copyright © 2018年 zxg. All rights reserved.
//

import UIKit
import Alamofire
import SwiftyJSON
import HandyJSON

class HttpRestfulClient {
    static let sharedInstance = HttpRestfulClient()
    
    private init() {}
    //    let device_id: Int = 6096495334
    //    let iid: Int = 5034850950

    
    //Demo
    //這裏使用了逃逸閉包,因爲responseJson爲異步,所以當網絡請求結果回來後,事實上testRequest()方法已經執行完了。那麼
    //傳入函數testRequest()的閉包是在testRequest()函數執行完後,纔要執行的,也就是說這個閉包已經逃逸到函數外部了。所以要加該註解
    //swift3.0默認是非逃逸的
    //弊端
    //該方法拿到數據後,字節解析字段,破壞了封裝性。合理的做法應該是封裝成model,再調用閉包,把model給出去
    public func testRequset<T:HandyJSON>(_ completionHandler: @escaping(_ dataFromNet:AnyObject)->(),_ protocol:BaseProtocol<T>){
        //        let params = ["device_id": device_id, "iid": iid]
        //        Alamofire.request("https://is.snssdk.com/search/suggest/homepage_suggest/?", method: .get, parameters params).responseString{
        //            (response) in
        //            if let value = response.result.value{
        //                print("value:",value5034850950)
        //                let json = JSON(value)
        //                print(json)
        //                guard json["message"] == "success" else {return}
        //
        //                if let data = json["data"].dictionary{
        //                    completionHandler(data["homepage_search_suggest"]!.string!)
        //                }
        //                let responseModel = BaseResponse<T>.deserialize(from: value)!
        //                print("model:",responseModel.data)
        //                completionHandler((responseModel.data as? AnyObject)!)
        //            }
        //        }
    }
    
    //網絡請求函數,參數固定,所有網絡請求參數放在netProtocol中,即使以後擴展
    //也不需要改變接口
    public func sendRequset<T:HandyJSON>(_ netProtocol:BaseProtocol<T>,_ competionHandler:@escaping(_ model:AnyObject?,_ error:NetError)->()){
        var error:NetError = NetError.SUCCESS
        Alamofire.request(netProtocol.getOpertion()!,
                          method: transformMethod(netProtocol.getMethod()),
                          parameters: netProtocol.getParams()).responseString{
                            (response) in
                            if let value = response.result.value{
                                print("value:",value)
                                let responseModel = BaseResponse<T>.deserialize(from: value)!
                                if(responseModel.message! != "success"){
                                    error = NetError.DATA_ERROR
                                }
                                competionHandler(responseModel.data as? AnyObject,error)
                            }
        }
    }
    
    //自定義NetMethod轉Alamofire.HTTPMethod,目的是對上層完全隱藏Alamofire,這樣即使換掉Alamofire框架
    //上層也不需要改動
    private func transformMethod(_ method:NetMethod)->HTTPMethod{
        switch method {
        case .GET:
            return HTTPMethod.get
        case .POST:
            return HTTPMethod.post
        default:
            return HTTPMethod.get
        }
    }
    
    enum NetError:Int{
        case SUCCESS = 0;
        case DATA_ERROR = 1;
    }
    
    enum NetMethod:Int{
        case GET = 10;
        case POST = 20;
    }
}

框架性代碼BaseProtocol

該類作爲sendRequest()的核心參數,封裝網絡請求參數,業務層開發時,不同的網絡請求均要實現這樣一個類

//
//  BaseProtocol.swift
//  HelloIOS
//
//  Created by zxg on 2018/10/29.
//  Copyright © 2018年 zxg. All rights reserved.
//

import UIKit
import HandyJSON

class BaseProtocol<T:HandyJSON> {
    //返回網絡請求參數
    func getParams()->Dictionary<String, AnyObject>?{
        return nil;
    }
    
    //返回網絡地址url
    func getOpertion()->String?{
        return nil
    }
    
    //返回請求方法
    func getMethod()->HttpRestfulClient.NetMethod{
        return HttpRestfulClient.NetMethod.GET
    }
    
    //以後可以增加接口

    public required init(){}
}

框架性代碼BaseResponse

用於使用HandyJSON進行Json解析,HandyJSON和Gson很像,都是採用反射、泛型等技術進行json與model的映射,不需要手動解析每一個字段,這裏使用泛型,也是爲了外部在調用時,通過泛型將需要映射成的目標model類傳進來,sendRequest內部進行解析。其實也可以直接將jsonStr拋給netProtocol,讓解析response這個動作也離散式的分解到每一個protocol中。而封裝BaseResponse的目的是做一些通用邏輯,例如在進行Model轉換前,判斷message是否爲success。是success再進行model轉換否則拋出error

//
//  BaseResponse.swift
//  HelloIOS
//
//  Created by zxg on 2018/10/31.
//  Copyright © 2018年 zxg. All rights reserved.
//

import Foundation
import HandyJSON

class BaseResponse<T>:HandyJSON{
    var message:String?
    var data:T?
    public required init(){}
}

業務性代碼

這裏我們站在業務端角度,使用一下該簡單框架,網絡請求的名字就叫TestNet吧

爲本次網絡請求配置Protocol類:TestNetProtocol

//
//  TestNetProtocol.swift
//  HelloIOS
//
//  Created by zxg on 2018/11/1.
//  Copyright © 2018年 zxg. All rights reserved.
//

import Foundation
import HandyJSON

class TestNetProtocol:BaseProtocol<TestNetResponse>{
    var device_id:Int=0
    var iid:Int=0
    
    override func getParams()->Dictionary<String, AnyObject>?{
        return ["device_id": device_id as AnyObject, "iid": iid as AnyObject];
    }
    
    override func getOpertion()->String?{
        return "https://is.snssdk.com/search/suggest/homepage_suggest/?"
    }

    override func getMethod() -> HttpRestfulClient.NetMethod {
        return HttpRestfulClient.NetMethod.GET
    }
    required init(){
        super.init()
    }
}

Response類

//
//  TestNetResponse.swift
//  HelloIOS
//
//  Created by zxg on 2018/10/31.
//  Copyright © 2018年 zxg. All rights reserved.
//

import Foundation
import HandyJSON
class TestNetResponse:HandyJSON{
    var call_per_refresh:Int?
    var homepage_search_suggest:String?
    var suggest_words:[SuggestWord]?
    required init(){}
}

class SuggestWord:HandyJSON{
    var id:Int?
    var or:String?
    var word:String?
    required init(){}
}

在Controller中調用

    //測試訪問網絡
    @IBAction func testNet(_ sender: Any) {
        prot.device_id = 6096495334
        prot.iid = 5034850950
        print("testNetWork click")
        HttpRestfulClient.sharedInstance.sendRequset(prot,{(model,error) in
            if error != HttpRestfulClient.NetError.SUCCESS {
                print("error")
                return
            }
            if let data = model as? TestNetResponse{
                self.updateUI(model: data)
            }
        })
    }

    private func updateUI(model:TestNetResponse){
        self.tvNetData.text = model.homepage_search_suggest!
    }

網絡返回的json:

testNetWork click
value: {"data":{"call_per_refresh":1,"homepage_search_suggest":"勞森 | 今晚中國女排直播5 | 古德洛克","suggest_words":[{"id":"6538744360526157064","or":"qcrs:65","word":"勞森"},{"id":"6605143180503422221","or":"qc:349 qcrs:15","word":"今晚中國女排直播5"},{"id":"6573947909807592707","or":"qcrs:66","word":"古德洛克"}]},"message":"success"}

後記

邏輯比較簡單,沒有處理緩存、header頭等邏輯,僅僅是爲了展示離散式網絡訪問思想的優點,以及練習下swift的泛型、閉包等技術。如果有時間,後期會豐富。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章