IOS拦截重定向请求(302)的几种方式

前言

在多数情况下,我们做的网络请求是返回200状态码的,但也有返回302的时候,比如使用基于Oauth2认证协议的API时,在认证阶段,需要提供一个回调地址,当用户授权后,服务器会返回一个302 Response,Response Header中会一个Location字段,包含了我们的回调地址,同时会有一个Code参数。我们在程序中该如何处理这个请求,并拿到这个Code参数呢。下面由我来为大家讲解下几种方式的做法,各取所需。

假设您知道并使用过Oauth2认证协议

(一)UIWebView控件

这是最常见的做法,但是UIWebView是无法拦截302请求的,只能等待整个流程完成回到回调地址时,我们在webView控件的webViewDidFinishLoad回调方法处理数据。

首先,我们需要让ViewController类继承UIWebViewDelegate协议,然后实现webViewDidFinishLoad方法:

1class WebLoginViewController: UIViewController,UIWebViewDelegate { 2 3 @IBOutlet var webView: UIWebView! 4 5 override func viewDidLoad() { 6 super.viewDidLoad() 7 8 webView.scalesPageToFit = true 9 webView.delegate = self 10 } 11 12 override func didReceiveMemoryWarning() { 13 super.didReceiveMemoryWarning() 14 } 15 16 func webViewDidFinishLoad(webView: UIWebView) { 17 //处理数据 18 } 19}

接着在启动时给webview一个加载地址,先载入指定的登陆页面:

1override func viewDidLoad() { 2 super.viewDidLoad() 3 webView.scalesPageToFit = true 4 webView.delegate = self 5 6 let url = "http://devonios.com" 7 //程序启动后,让webview加载 OSChina的验证登陆界面 8 webView.loadRequest(NSURLRequest(URL: NSURL(string: url)!)) 9}

当整个请求链完成后,我们在DidFinishLoad中通过判断请求的url,来确认是否已经回到了回调地址上

1func webViewDidFinishLoad(webView: UIWebView) { 2 3 var url = webView.request?.URL!.absoluteString 4 5 if url!.hasPrefix("回调地址url") 6 { 7 //从一个url字符串中拿到Code值 8 let code = url!.GetCodeL() 9 println("code = \(code)") 10 11 //拿到Code后,可以开始请求Token了 12 } 13}

很显然,这种方法还需要等待webView来处理回调地址的请求,而这个请求对我们的程序来说是完全没有必要的。

我们要做的是拦截 302!

(二)基于NSURLConnection来设置拦截

在很多教程中都提到了NSURLConnection,它可以发送一个请求,比如:

1let request = NSURLRequest(URL: NSURL(string: "http://devonios.com")!) 2NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) { (response, data, 3error) -> Void in 4 //处理返回数据 5}

如果要发送POST的话,需要使用可编辑的 NSMutableURLRequest类(它是继承NSURLRequest类的)。

我们需要的拦截效果,其实就是要给NSURLConnection设置一个delegate,提供一个事件发生时的回调方法。

NSURLConnection类有一个构造函数:

init?(request request: NSURLRequest, delegate delegate: AnyObject?)

第二个参数就是我们需要设置的delegate。对应的delegate是: NSURLConnectionDataDelegate。

我们在Dask中可以看到它有这些东西:

开始写代码了:

1class LoginViewController: UIViewController,NSURLConnectionDataDelegate { 2 func connection(){ 3 //创建一个可以编辑的NSURLRequest 4 var mutableRequest = NSMutableURLRequest(URL: NSURL(string: "http://devonios.com")!) 5 mutableRequest.HTTPMethod = "POST" 6 //设置POST请求的表单数据 7 mutableRequest.HTTPBody = paramString.dataUsingEncoding(NSStringEncoding.allZeros, allowLossyConversion: true) 8 //使用构造函数方法创建一个NSURLConnection的实例 9 var connection:NSURLConnection = NSURLConnection(request: mutableRequest, delegate: self)! 10 connection.start() 11 } 12 //处理重定向请求的方法 13 func connection(connection: NSURLConnection, willSendRequest request: NSURLRequest, redirectResponse response: NSURLResponse?) -> NSURLRequest? { 14 15 if let r = response{ 16 17 //当前重定向请求的url,包含了Code参数 18 let requesturl = request.URLString 19 20 //得到Code,由于Code参数设置了属性观察器,所以当Code被赋值时,会自动去获取Token 21 self.code = requesturl.GetCode() 22 23 //因为已经拿到Code了,所以拦截掉当前这个重定向请求,直接返回nil 24 return nil 25 } 26 return request 27 } 28 //整个请求完成后,即拦截到302后,不再请求了就返回这里 29 func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) { 30 if (某些判断条件){ 31 self.navigationController?.popViewControllerAnimated(true) 32 } 33 } 34}

(三)基于NSURLSession类来设置拦截

NSURLSession是IOS 7中开始出现的全新的网络接口类,和NSURLConnection类似,同样需要设置delegate。

1class MyRequestController:NSObject,NSURLSessionTaskDelegate { 2 3 let session:NSURLSession? 4 5 init(){ 6 let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration() 7 session = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil) 8 } 9 10 deinit{ 11 session!.invalidateAndCancel() 12 } 13 //处理重定向请求,直接使用nil来取消重定向请求 14 func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: (NSURLRequest!) -> Void) { 15 completionHandler(nil) 16 } 17 18 func sendRequest() { 19 20 var URL = NSURL(string: "http://devonios.com") 21 let request = NSMutableURLRequest(URL: URL!) 22 request.HTTPMethod = "POST" 23 24 request.HTTPBody = paramString.dataUsingEncoding(NSStringEncoding.allZeros, allowLossyConversion: true) 25 26 let task = session!.dataTaskWithRequest(request, completionHandler: { (data : NSData!, response : NSURLResponse!, error : NSError!) -> Void in 27 //由于拦截了302,设置了completionHandler参数为nil,所以忽略了重定向请求,这里返回的Response就是包含302状态码的Response了。 28 let resp:NSHTTPURLResponse = response as! NSHTTPURLResponse 29 println("包含302状态的Response Header字段 : \(resp.allHeaderFields)") }) 30 task.resume() 31 } 32}

目前为止,我们通过为NSURLConnection或者NSURLSession设置一个Delegate,通过回调方法来拦截(其实就是返回个nil)。

但是在一个项目中,我们通常会使用Alamofire这种第三库来操作网络请求,我要是再自己再重新写个请求,那岂不是很麻烦?

(四)完善Alamofire库,实现拦截302请求

Alamofire啥就不多说了,分析它的代码可以发现,是使用NSURLSession来实现请求的。

既然如此,那么我们就要找到NSURLSession,为它设置delegate,然后重写willPerformHttpRedirection。

在Alamofire.swift文件中,request方法是暴露给我们调用的,Manager类的sharedInstance属性来管理自身对象。

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

Manager.sharedInstance属性的实现,定义了请求头信息,然后调用构造函数

1public static let sharedInstance: Manager = { 2 let configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration() 3 configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders 4 return Manager(configuration: configuration) 5 }()

构造函数,我们要找的NSURLSession就在这里,它默认已经有了一个Class(SessionDelegate)来实现相应的delegate了:

1required public init(configuration: NSURLSessionConfiguration? = nil) { 2 self.delegate = SessionDelegate() 3 self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: 4nil) 5 self.delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in 6 if let strongSelf = self { 7 strongSelf.backgroundCompletionHandler?() 8 } 9 } 10}

这个构造函数看上去动不了什么,关键还在SessionDelegate类,它实现了所有了NSURLSessionDelegate:

1public final class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate { 2 public var taskWillPerformHTTPRedirection: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse,NSURLRequest) -> NSURLRequest?)? 3 public func URLSession(session: NSURLSession, task: NSURLSessionTask, willPerformHTTPRedirection response: NSHTTPURLResponse, newRequest request: NSURLRequest, completionHandler: ((NSURLRequest!) -> Void)) { 4 var redirectRequest: NSURLRequest? = request 5 if taskWillPerformHTTPRedirection != nil { 6 redirectRequest = taskWillPerformHTTPRedirection!(session, task, response, request) 7 } 8 completionHandler(redirectRequest) 9 } 10}

仔细观察会发现,有一个public的 变量(var)taskWillPerformHTTPRedirection、有一个重写方法(willperformHTTPRedirection )。

从这个方法中可以看出,它期望我们给taskWillPerformHTTPRedirection变量传一个自定义方法,如果我们赋值了,它就运行我们的自定义方法。

我们要给taskWillPerformHTTPRedirection变量赋值,参数是一个方法。

在Manager类中加入下面代码:

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

对Alamofire库的修改就这样可以了!

我们需要在发送网络请求前,先调用setTaskWillRedirectAction方法,传入我们的自定义方法。

使用方法:

1var manager = Manager.sharedInstance 2manager.setTaskWillRedirectAction { (session, task, response, request) -> NSURLRequest? in 3 return nil 4} 5manager.request(Method.POST, url, parameters: authparam.toDictionary(), encoding: ParameterEncoding.URL).response { (request, response, data, err) -> Void in 6 //由于上面的setTaskWillRedirectAction方法返回nil,所以在处理NSURLSessionDataDelegate的重写方法时,complectionHandler方法参数为nil,也就实现了拦截! 7 println(response?.allHeaderFields["Location"]) 8}

注意,这里需要先从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标签,所以无法显示图片,详见。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

IOS拦截重定向请求(302)的几种方式 - HelloWorld