上手Alamofire處理HTTP請求

向大家介紹Alamofire的基本用法魂角,即如何通過iOS客戶端發(fā)起各種HTTP請求剩胁,并以Alamofire提供的不同方式處理HTTP response截粗。為了簡單起見,在我們的例子里蟋定,Alamofire的應(yīng)用都不會和UI代碼相關(guān),我們只演示HTTP通信的部分垢夹。首先溢吻,我們從一個最簡單的HTTP請求開始维费。


發(fā)起一個最基本的HTTP請求

打開AlamoFireDemo的ViewController.swift文件果元,添加下面的代碼:


import UIKit
import Alamofire

"引入"了Alamofire module之后,我們就可以使用它提供的各種API了犀盟。為了測試各種HTTP action而晒,我們需要一個與之配合的服務(wù)端程序。幸運的是阅畴,至少現(xiàn)在我們還不用專門去寫它倡怎,httpbin.org為我們提供了方便的HTTP測試API:

image

包括GET一個頁面、一個JSON贱枣、各種REST方法监署、常用的HTTP status、認證纽哥、下載等钠乏。它們可以很方便的讓我們來測試Alamofire提供的各種功能。我們從GET /ip開始春塌,httpbin會返回一個類似下面這樣的JSON:


{
    "origin": "106.185.45.242"
}

回到Xcode晓避,在viewDidLoad()里,添加下面的代碼:


override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    Alamofire.request(.GET, "https://httpbin.org/ip")
}

這樣只壳,我們就完成了一次對httpbin.org的GET請求俏拱。看似簡單吼句,卻有三點需要說明:

第一點是Alamofire.request這樣的用法锅必,該如何理解呢?Alamofire是類名嗎惕艳?request是一個class method嗎搞隐?實際上不是這樣的。前面的Alamofire是因為我們一開始引入了Alamofire module之后尔艇,開啟的名字空間尔许;而request則是這個名字空間里的一個函數(shù),我們可以在Alamofire的源代碼里找到它终娃。

第二點是request的第一個參數(shù)味廊,表示我們采用的HTTP方法。它是Alamofire定義的一個enum,在這里


public enum Method: String {
    case OPTIONS, GET, HEAD, POST, PUT, PATCH, DELETE, TRACE, CONNECT
}

第三點是Alamofire發(fā)起的HTTP request是和App主線程并行執(zhí)行的余佛,它并不會阻塞App的UI柠新。

理解了這三點,上面的代碼就沒有任何問題了辉巡。接下來恨憎,我們來看如何處理HTTP response。


處理HTTP response

Alamofire有一個最原始的獲取HTTP response的方法郊楣,像這樣:


override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    Alamofire.request(.GET, "https://httpbin.org/ip")
        .response { request, response, data, error in 
            print(request)
            print(response)
            print(data)
            print(error)
        }
}

image

從圖中的結(jié)果就可以看到closure的四個參數(shù)對應(yīng)的類型了憔恳,它們分別代表request/response的基本信息,返回內(nèi)容的原始數(shù)據(jù)以及錯誤信息净蚤。事實上钥组,如果我們查看response的實現(xiàn),就會發(fā)現(xiàn)更多內(nèi)容:


public func response(
    queue queue: dispatch_queue_t? = nil,
    completionHandler: 
        (NSURLRequest?, 
         NSHTTPURLResponse?, 
         NSData?, 
         NSError?) -> Void)
    -> Self
{
    delegate.queue.addOperationWithBlock {
        dispatch_async(queue ?? dispatch_get_main_queue()) {
            completionHandler(self.request, 
                self.response, 
                self.delegate.data, 
                self.delegate.error)
        }
    }

    return self
}

response實際上有2個參數(shù)今瀑,只不過第一個參數(shù)有默認值程梦,所以,我們使用了一個trailing closure定義了它的第二個參數(shù)橘荠。當(dāng)?shù)谝粋€參數(shù)queue為nil時屿附,Alamofire默認會在App的主線程中執(zhí)行我們的handler,方便我們在收到HTTP請求的時候更新UI哥童。

當(dāng)我們通過這種方式得到了一個表示HTTP response的NSData對象后挺份,我們就可以把它變成Image、序列化成JSON等等如蚜。但是如果真是這樣压恒,我們就完全沒必要使用Alamofire了,因為這幾乎和NSURLSession的"套路"是一樣的错邦。實際上探赫,我們很少直接使用這個原始的response,Alamofire為我們提供了更方便的序列化結(jié)果的方法:


responseData(/*completionHandler*/)
responseString(encoding: NSStringEncoding, 
    /*completionHandler*/)
responseJSON(options: NSJSONReadingOptions, 
    /*completionHandler*/)
responsePropertyList(options: NSPropertyListReadOptions, 
    /*completionHandler*/)

image

它們之中撬呢,后三個方法的第一個參數(shù)可以先暫時忽略伦吠,因為它們都是有默認值的。這四個方法的共同之處就是它們有一個共同的handler魂拦,這個handler接受一個類型為Response<nsdata, nserror="" style="box-sizing: border-box;">的參數(shù)毛仪,用來讓我們在Alamofire完成序列化之后,做進一步的處理芯勘。</nsdata,>

而我們查看Response的源代碼就會發(fā)現(xiàn)箱靴,它封裝了之前我們用過的request / response / data:


public struct Response {
    /// The URL request sent to the server.
    public let request: NSURLRequest?

    /// The server's response to the URL request.
    public let response: NSHTTPURLResponse?

    /// The data returned by the server.
    public let data: NSData?

    /// The result of response serialization.
    public let result: Result

    // Omit for simplicity...
}

并且,它還額外提供了一個Result<value, error="" style="box-sizing: border-box;">類型的屬性result荷愕,方便我們讀取序列化過之后的結(jié)果(簡單起見衡怀,例子中省略了實現(xiàn)的部分):</value,>


public enum Result {
    case Success(Value)
    case Failure(Error)

    public var isSuccess: Bool {
        // Omit for simplicity...    
    }

    public var isFailure: Bool {
        // Omit for simplicity...   
    }

    public var value: Value? {
        // Omit for simplicity...
    }

    public var error: Error? {
        // Omit for simplicity...
    }
}

另外棍矛,Result還實現(xiàn)了CustomStringConvertible protocol,我們可以直接print一個Result對象抛杨。

在理解了這些處理HTTP response的類型之后够委,我們就可以很方便的處理Alamofire返回的序列化結(jié)果了。


處理序列化之后的String和JSON

首先來看String:


Alamofire.request(.GET, "https://httpbin.org/ip")
    .response { request, response, data, error in
        print(request)
        print(response)
        print(data)
        print(data.dynamicType)
        print(error)
        print(error.dynamicType)
    }
    .responseString { response in
        print("String:====================")

        switch response.result {
        case .Success(let str):
            print("\(str.dynamicType)")
        case .Failure(let error):
            print("\(error)")
        }
    }

在這里怖现,Alamofire的各種response方法是可以串聯(lián)在一起的茁帽,由于response.result是一個enum,我們可以通過一個switch...case來處理它的兩種結(jié)果屈嗤,并且在.Success里潘拨,直接讀取它的associated value作為返回的字符串;在.Failure里讀取包含錯誤信息的對象恢共。

image

接下來战秋,我們來看JSON。為了得到一個相對復(fù)雜一些的JSON讨韭,這次,我們請求httpbin的/get癣蟋,然后透硝,會得到一個類似下面的返回結(jié)果:


{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 
    "Accept-Encoding": "gzip, deflate, sdch", 
    "Accept-Language": "en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4", 
    "Cookie": "_ga=GA1.2.1123360294.1453608486; _gat=1", 
    "Host": "httpbin.org", 
    "Referer": "https://httpbin.org/", 
    "Upgrade-Insecure-Requests": "1", 
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2646.0 Safari/537.36"
  }, 
  "origin": "106.185.45.242", 
  "url": "https://httpbin.org/get"
}

首先我們把之前例子里的請求的URL修改成https://httpbin.org/get


Alamofire.request(.GET, "https://httpbin.org/get") 

然后,把responseJSON串聯(lián)在responseString后面:


Alamofire.request(.GET, "https://httpbin.org/get")
    .responseString(completionHandler: { response in
        print("String ===============")

        switch response.result {
        case .Success(let str):
            print("\(str.dynamicType)")
            print("\(str)")
        case .Failure(let error):
            print("\(error)")
        }
    })
    .responseJSON(completionHandler: { response in
        print("JSON ================")

    })

為了更好的理解解析JSON的方式疯搅,我們來看個圖:

image

對于一個JSON的返回結(jié)果來說濒生,它的所有KEY的類型,都是String幔欧,而VALUE的類型罪治,有可能是一個String值,有可能是一個數(shù)組礁蔗,還有可能是一個對象觉义。因此,Alamofire只能用一個AnyObject類型的對象表示它浴井。通常晒骇,我們要把它轉(zhuǎn)化成一個<key style="box-sizing: border-box;">NSDictionary</key>或Dictionary<String, AnyObject>類型的對象。然后偏化,根據(jù)訪問的KEY的類型励翼,做進一步的類型轉(zhuǎn)換:

  • 讀取key1時闲先,把結(jié)果轉(zhuǎn)化成String
  • 讀取key2時,把結(jié)果轉(zhuǎn)化成Array
  • 讀取key3時瘤缩,把結(jié)果轉(zhuǎn)化成其它的Dictionary

理解了處理JSON的方法,我們就可以在responseJSON中處理httpbin的返回結(jié)果了:


Alamofire.request(.GET, "https://httpbin.org/get")
    .responseJSON(completionHandler: { response in
        print("JSON ================")

        switch response.result {
        case .Success(let json):
            let dict = json as! Dictionary
            let origin = dict["origin"] as! String
            let headers = dict["headers"] as! Dictionary

            print("origin: \(origin)")
            let ua = headers["User-Agent"]
            print("UA: \(ua)")
        case .Failure(let error):
            print("\(error)")
        }
    })

在上面的代碼中我們可以看到伦泥,在讀取"origin"和"headers"時剥啤,我們并不用類型轉(zhuǎn)換何暮,并且,在下面的結(jié)果里铐殃,變量origin和ua各自代表了JSON中的內(nèi)容:

image

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末海洼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子富腊,更是在濱河造成了極大的恐慌坏逢,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赘被,死亡現(xiàn)場離奇詭異是整,居然都是意外死亡,警方通過查閱死者的電腦和手機民假,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門浮入,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人羊异,你說我怎么就攤上這事事秀。” “怎么了野舶?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵易迹,是天一觀的道長。 經(jīng)常有香客問我平道,道長睹欲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任一屋,我火速辦了婚禮窘疮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冀墨。我一直安慰自己闸衫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布轧苫。 她就那樣靜靜地躺著楚堤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪含懊。 梳的紋絲不亂的頭發(fā)上身冬,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機與錄音岔乔,去河邊找鬼酥筝。 笑死,一個胖子當(dāng)著我的面吹牛雏门,可吹牛的內(nèi)容都是我干的嘿歌。 我是一名探鬼主播掸掏,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宙帝!你這毒婦竟也來了丧凤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤步脓,失蹤者是張志新(化名)和其女友劉穎愿待,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體靴患,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡仍侥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鸳君。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片农渊。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖或颊,靈堂內(nèi)的尸體忽然破棺而出砸紊,到底是詐尸還是另有隱情,我是刑警寧澤饭宾,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布批糟,位于F島的核電站,受9級特大地震影響看铆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盛末,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一弹惦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悄但,春花似錦棠隐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嚎京,卻和暖如春嗡贺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鞍帝。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工诫睬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帕涌。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓摄凡,卻偏偏與公主長得像续徽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子亲澡,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容