说起网络框架,大家第一时间就会想到 AFNetworking、Alamofire 这些业内响当当的作品,有的老鸟也会适当伤感一下曾经用的 ASI 。这些框架都有一个共同点——功能都很复杂,很齐全,而我们往往只能用到很小很小的一个部分。
事实上,咱们做 App 的时候,绝大多数时候对网络的需求都是收发 GET/POST 请求。就这样来看,根据需求来造个属于自己的轮子,似乎也是个不错的选择。尤其是现在苹果提供的 NSURLSession
已经非常强大,基于原生的 SDK 来做一个自己的框架,其实是很容易的。
根据这个思想,我之前撸了一个简单的网络库 AaHTTP,在工作的项目里重度用了一段时间也没有遇到什么特别的问题。
现在我们就来一步步看看如何做一个属于自己的简单的网络框架。
发送请求的步骤分析
要发送一个请求,分为如下步骤:
- 如果携带的参数是
GET
类型,则将参数进行 URL encode(转化为y1=x1&y2=x2
的形式),追加到原始 url 的后面。如果参数是POST
类型,则 URL 不变。 - 用最新的 URL 生成一个
NSMutableURLRequest
的对象 - 如果参数是
POST
的情况,设置Content-Type
为application/x-www-form-urlencoded
, 并将参数进行 URL encode,并添加到 body 中。 - 使用 NSURLSession 发送该请求
URL encode时,需要对特殊字符进行转义。
定义发送请求的接口
根据上面的步骤,我们不难一步到位的实现发送请求,新建一个 AaNet.swift
(名字您随意),并声明我们的类方法:
1 2 3 4 5 6 7 8 9 |
class AaNet: NSObject { class func request( method : String = "GET",url : String ,form : Dictionary = [:],success : (data : NSData?)->Void,fail:(error : NSError?)->Void){ } func buildParams(parameters: [String: AnyObject]) -> String { return "" } } |
我们首先声明了两个函数,request
函数接受的参数依次是:
method
: 请求类别url
: 目标地址form
: 参数表success
: 成功的回调, 类型为(data:NSData?) -> Void
fail
: 失败的回调,类型为(error : NSError?) -> Void
第二个函数 buildParams
, 输入一个字典,返回一个字符串。很容易想到就是我们用来做 url encode 的函数。
建议大家写代码前,都先写出主要函数的声明和对应的参数、返回值的类型。这其实就是一种最基本的架构工作
实现发送请求
现在按照之前的分析,我们来实现请求发送的逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
class func request( method : String = "GET",url : String ,form : Dictionary = [:],success : (data : NSData?)->Void,fail:(error : NSError?)->Void){ var innerUrl = url if method == "GET"{ innerUrl += "?" + AaNet().buildParams(form) } let req = NSMutableURLRequest(URL: NSURL(string: innerUrl)!) req.HTTPMethod = method if method == "POST" { req.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") print("POST PARAMS (form)") req.HTTPBody = AaNet().buildParams(form).dataUsingEncoding(NSUTF8StringEncoding) } let session = NSURLSession.sharedSession() print(req.description) let task = session.dataTaskWithRequest(req) { (data, response, error) -> Void in if error != nil{ fail(error: error) print(response) }else{ if (response as! NSHTTPURLResponse).statusCode == 200{ success(data : data) }else{ fail(error: error) print(response) } } } task.resume() } |
整个流程很直观,虽然 GET 参数和 POST 参数处理的位置不同,但都是用我们的 url encode 函数 buildParams
来操作的。区别是 GET 请求的话,处理完后直接 append 到 url 后面,而 POST 需要用 UTF8 encode 一下,放在 request 的 body 里。
然后用 NSURLSession 的默认 session: sharedSession()
来发送请求,并在回调里判断 statusCode 以及 error 对象是否为 nil 来判断请求是否为空,来分别调用我们的 success
回调或 fail
回调。
实现 URL encode
现在我们来实现 buildParams
,大体的步骤为:
encode:
- 把输入字典转换为键值对的数组。
[ (Key,Value) ]
- 对于每一个
(key,value)
,执行:
2.1 对key
进行转义,得到key'
2.2 检查value
的类型,如果是简单的值,则对其进行转义,得到value'
。并将(key' , value')
输出到结果数组中。
2.3 如果value
是数组,则用当前的key
和value
中的每一个元素组成 tuple:[(key,subValue)]
, 递归执行步骤2。
2.4 如果value
是字典,也先把value
对应的字段转化为键值对数组,但是 key 的形式为key[subKey]
, 前面是 key 是当前的 key,subKey 代表value
对应的字典中的 key。得到键值对数组后,递归执行步骤2。 - 步骤2执行完毕后,我们会得到一个一维的、并且 key 和 value 都被转义过的键值对数组
[ (key,value) ]
,然后我们将其转换为key1=value1&key2=value2&...keyN=valueN
的形式返回。
仔细感受一下,步骤2是不是有一个
flat
的过程。
我们先实现转义: