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的泛型、闭包等技术。如果有时间,后期会丰富。

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页