IOS攔截重新導向請求(302)的幾種方式

來源:互聯網
上載者:User

標籤:

前言

在多數情況下,我們做的網路請求是返回200狀態代碼的,但也有返回302的時候,比如使用基於Oauth2認證協議的API時,在認證階段,需要提供一個回調地址,當使用者授權後,伺服器會返回一個302 Response,Response Header中會一個Location欄位,包含了我們的回調地址,同時會有一個Code參數。我們在程式中該如何處理這個請求,並拿到這個Code參數呢。下面由我來為大家講解下幾種方式的做法,各取所需。

假設您知道並使用過Oauth2認證協議
(一)UIWebView控制項

這是最常見的做法,但是UIWebView是無法攔截302請求的,只能等待整個流程完成回到回調地址時,我們在webView控制項的webViewDidFinishLoad回調方法處理資料。

首先,我們需要讓ViewController類繼承UIWebViewDelegate協議,然後實現webViewDidFinishLoad方法:

class WebLoginViewController: UIViewController,UIWebViewDelegate {    @IBOutlet var webView: UIWebView!        override func viewDidLoad() {        super.viewDidLoad()        webView.scalesPageToFit = true        webView.delegate = self    }    override func didReceiveMemoryWarning() {        super.didReceiveMemoryWarning()    }        func webViewDidFinishLoad(webView: UIWebView) {        //處理資料    }}
接著在啟動時給webview一個載入地址,先載入指定的登陸頁面:

override func viewDidLoad() {    super.viewDidLoad()    webView.scalesPageToFit = true    webView.delegate = self        let url = "https://www.oschina.net/xxxxxx"    //程式啟動後,讓webview載入 OSChina的驗證登陸介面    webView.loadRequest(NSURLRequest(URL: NSURL(string: url)!))}

當整個請求鏈完成後,我們在DidFinishLoad中通過判斷請求的url,來確認是否已經回到了回調地址上

func webViewDidFinishLoad(webView: UIWebView) {        var url = webView.request?.URL!.absoluteString        if url!.hasPrefix("回調地址url")    {        //從一個url字串中拿到Code值        let code = url!.GetCodeL()        println("code = \(code)")                //拿到Code後,可以開始請求Token了    }}

很顯然,這種方法還需要等待webView來處理回調地址的請求,而這個請求對我們的程式來說是完全沒有必要的。

我們要做的是攔截 302!

(二)基於NSURLConnection來設定攔截

在很多教程中都提到了NSURLConnection,它可以發送一個請求,比如:

let request = NSURLRequest(URL: NSURL(string: "http://devonios.com")!)NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) { (response, data,error) -> Void in    //處理返回資料}
如果要發送POST的話,需要使用可編輯的 NSMutableURLRequest類(它是繼承NSURLRequest類的)。

我們需要的攔截效果,其實就是要給NSURLConnection設定一個delegate,提供一個事件發生時的回調方法。

NSURLConnection類有一個建構函式:

init?(request request: NSURLRequest, delegate delegate: AnyObject?)
第二個參數就是我們需要設定的delegate。對應的delegate是: NSURLConnectionDataDelegate

我們在Dask中可以看到它有這些東西:

開始寫代碼了:

class LoginViewController: UIViewController,NSURLConnectionDataDelegate {    func connection(){        //建立一個可以編輯的NSURLRequest        var mutableRequest = NSMutableURLRequest(URL: NSURL(string: "http://devonios.com")!)        mutableRequest.HTTPMethod = "POST"        //設定POST請求的表單資料        mutableRequest.HTTPBody = paramString.dataUsingEncoding(NSStringEncoding.allZeros, allowLossyConversion: true)        //使用建構函式方法建立一個NSURLConnection的執行個體        var connection:NSURLConnection = NSURLConnection(request: mutableRequest, delegate: self)!        connection.start()    }    //處理重新導向請求的方法    func connection(connection: NSURLConnection, willSendRequest request: NSURLRequest, redirectResponse response: NSURLResponse?) -> NSURLRequest? {        if let r = response{                        //當前重新導向請求的url,包含了Code參數            let requesturl = request.URLString                        //得到Code,由於Code參數設定了屬性觀察器,所以當Code被賦值時,會自動去擷取Token            self.code = requesturl.GetCode()                        //因為已經拿到Code了,所以攔截掉當前這個重新導向請求,直接返回nil            return nil        }        return request    }    //整個請求完成後,即攔截到302後,不再請求了就返回這裡    func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {        if (某些判斷條件){            self.navigationController?.popViewControllerAnimated(true)        }    }}
(三)基於NSURLSession類來設定攔截

NSURLSession是IOS 7中開始出現的全新的網路介面類,和NSURLConnection類似,同樣需要設定delegate。

class MyRequestController:NSObject,NSURLSessionTaskDelegate {        let session:NSURLSession?        init(){        let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()        session = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)    }        deinit{        session!.invalidateAndCancel()    }    //處理重新導向請求,直接使用nil來取消重新導向請求    func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: (NSURLRequest!) -> Void) {        completionHandler(nil)    }        func sendRequest() {        var URL = NSURL(string: "http://devonios.com")        let request = NSMutableURLRequest(URL: URL!)        request.HTTPMethod = "POST"                request.HTTPBody = paramString.dataUsingEncoding(NSStringEncoding.allZeros, allowLossyConversion: true)                let task = session!.dataTaskWithRequest(request, completionHandler: { (data : NSData!, response : NSURLResponse!, error : NSError!) -> Void in            //由於攔截了302,設定了completionHandler參數為nil,所以忽略了重新導向請求,這裡返回的Response就是包含302狀態代碼的Response了。            let resp:NSHTTPURLResponse = response as! NSHTTPURLResponse            println("包含302狀態的Response Header欄位 : \(resp.allHeaderFields)")  })            task.resume()    }}

目前為止,我們通過為NSURLConnection或者NSURLSession設定一個Delegate,通過回調方法來攔截(其實就是返回個nil)。

但是在一個項目中,我們通常會使用Alamofire這種第三庫來操作網路請求,我要是再自己再重新寫個請求,那豈不是很麻煩?

(四)完善Alamofire庫,實現攔截302請求

Alamofire啥就不多說了,分析它的代碼可以發現,是使用NSURLSession來實現請求的。

既然如此,那麼我們就要找到NSURLSession,為它設定delegate,然後重寫willPerformHttpRedirection。

在Alamofire.swift檔案中,request方法是暴露給我們調用的,Manager類的sharedInstance屬性來管理自身對象。

public func request(method: Method, URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {    return Manager.sharedInstance.request(method, URLString, parameters: parameters, encoding: encoding)}

Manager.sharedInstance屬性的實現,定義了要求標頭資訊,然後調用建構函式

public static let sharedInstance: Manager = {     let configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()     configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders     return Manager(configuration: configuration) }()
建構函式,我們要找的NSURLSession就在這裡,它預設已經有了一個Class(SessionDelegate)來實現相應的delegate了:
required public init(configuration: NSURLSessionConfiguration? = nil) {    self.delegate = SessionDelegate()    self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue:nil)    self.delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in        if let strongSelf = self {            strongSelf.backgroundCompletionHandler?()        }    }}
這個建構函式看上去動不了什麼,關鍵還在SessionDelegate類,它實現了所有了NSURLSessionDelegate:
public final class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {    public var taskWillPerformHTTPRedirection: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse,NSURLRequest) -> NSURLRequest?)?    public func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: ((NSURLRequest!) -> Void)) {     var redirectRequest: NSURLRequest? = request     if taskWillPerformHTTPRedirection != nil {         redirectRequest = taskWillPerformHTTPRedirection!(session, task, response, request)     }     completionHandler(redirectRequest) }}
仔細觀察會發現,有一個public的 變數(var)taskWillPerformHTTPRedirection、有一個重寫方法(willperformHTTPRedirection )。

從這個方法中可以看出,它期望我們給taskWillPerformHTTPRedirection變數傳一個自訂方法,如果我們賦值了,它就運行我們的自訂方法。

我們要給taskWillPerformHTTPRedirection變數賦值,參數是一個方法。

在Manager類中加入下面代碼:

public typealias TaskWillRedirectAction = ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse,NSURLRequest) -> NSURLRequest?)public func setTaskWillRedirectAction(action:TaskWillRedirectAction){    self.delegate.taskWillPerformHTTPRedirection = action}

對Alamofire庫的修改就這樣可以了!

我們需要在發送網路請求前,先調用setTaskWillRedirectAction方法,傳入我們的自訂方法。

使用方法:

var manager = Manager.sharedInstancemanager.setTaskWillRedirectAction { (session, task, response, request) -> NSURLRequest? in    return nil}manager.request(Method.POST, url, parameters: authparam.toDictionary(), encoding: ParameterEncoding.URL).response { (request, response, data, err) -> Void in    //由於上面的setTaskWillRedirectAction方法返回nil,所以在處理NSURLSessionDataDelegate的重寫方法時,complectionHandler方法參數為nil,也就實現了攔截!    println(response?.allHeaderFields["Location"])}

注意,這裡需要先從sharedInstance屬性中拿到一個Manager對象,然後再用這個對象設定攔截的回調方法,再發送請求。

如果您還是使用Alamofire.request來發送請求的話,就沒有作用了,因為你又重新建立了個Manager類對象。

參考資料

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/RequestChanges.html

http://stackoverflow.com/questions/1446509/handling-redirects-correctly-with-nsurlconnection

tips:

本文由wp2osc匯入,原文連結:http://devonios.com/intercept-302-request.html

由於OSChina的OpenAPI在處理content參數時會自動過濾img標籤,所以無法顯示圖片,詳見

IOS攔截重新導向請求(302)的幾種方式

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.