Objective
In most cases, the network request we make is to return a 200 status code, but also return 302, such as when using an API based on the OAUTH2 authentication protocol, in the authentication phase, you need to provide a callback address, when the user authorization, the server will return a 302 Response, A location field in the Response header contains our callback address with a code parameter. How do we handle this request in the program and get the code parameter? Let me explain the following ways for you, picking.
assume that you know and have used the OAUTH2 authentication protocol
first. UIWebView controls
This is the most common practice, but UIWebView is unable to intercept the 302 request and only waits for the entire process to complete back to the callback address, we process the data in the Webviewdidfinishload callback method of the WebView control.
First, we need to have the Viewcontroller class inherit the uiwebviewdelegate protocol and then implement the webviewdidfinishload method:
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) {
//Data processing
}
}
Then give WebView a load address at startup, loading the specified landing page first:
Override func viewDidLoad() {
super.viewDidLoad()
webView.scalesPageToFit = true
webView.delegate = self
Let url = "https://www.oschina.net/xxxxxx"
/ / After the program starts, let the webview load OSChina's verification login interface
webView.loadRequest(NSURLRequest(URL: NSURL(string: url)!))
}
When the entire request chain is complete, we confirm that the requested URL is returned to the callback address in Didfinishload.
Func webViewDidFinishLoad(webView: UIWebView) {
Var url = webView.request?.URL!.absoluteString
If url!.hasPrefix("callback address url")
{
/ / Get the Code value from a url string
Let code = url!.GetCodeL()
Println("code = \(code)")
/ / Get the Code, you can start to request Token
}
}
Obviously, this approach also needs to wait for WebView to handle the callback address request, which is completely unnecessary for our program.
All we have to do is intercept 302!
second.Setting up interception based on Nsurlconnection
Nsurlconnection is mentioned in many tutorials, which can send a request such as:
Let request = NSURLRequest(URL: NSURL(string: "http://devonios.com")!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) { (response, data,
Error) -> Void in
/ / Processing return data
}
If you want to send a post, you need to use an editable
nsmutableurlrequestClass (which is inherited from the Nsurlrequest class).
The interception effect we need is to set a delegate for Nsurlconnection, which provides a callback method when an event occurs.
The Nsurlconnection class has a constructor:
Init? (Request Request:nsurlrequest, delegate delegate:anyobject?)
The second parameter is the delegate we need to set. The corresponding delegate are:
nsurlconnectiondatadelegate.
We can see in the dask that it has these things:
Start writing code:
Class LoginViewController: UIViewController, NSURLConnectionDataDelegate {
Func connection(){
/ / Create an NSURLRequest that can be edited
Var mutableRequest = NSMutableURLRequest(URL: NSURL(string: "http://devonios.com")!)
mutableRequest.HTTPMethod = "POST"
/ / Set the form data of the POST request
mutableRequest.HTTPBody = paramString.dataUsingEncoding(NSStringEncoding.allZeros, allowLossyConversion: true)
/ / Use the constructor method to create an instance of NSURLConnection
Var connection:NSURLConnection = NSURLConnection(request: mutableRequest, delegate: self)!
Connection.start()
}
/ / Method of handling redirect requests
Func connection(connection: NSURLConnection, willSendRequest request: NSURLRequest, redirectResponse response: NSURLResponse?) -> NSURLRequest? {
If let r = response{
/ / The current url of the redirect request, including the Code parameter
Let requesturl = request.URLString
/ / Get Code, because the Code parameter sets the property observer, so when the Code is assigned, it will automatically get the Token
Self.code = requesturl.GetCode()
//Because I have already got the Code, I intercept the current redirect request and return nil directly.
Return nil
}
Return request
}
//After the entire request is completed, it will be intercepted to 302, and will be returned if no longer requested.
Func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
If (some judgment conditions) {
self.navigationController?.popViewControllerAnimated(true)
}
}
}
third. Setting up interception based on the Nsurlsession class
Nsurlsession is a new network interface class that starts in iOS 7, similar to Nsurlconnection, and also requires setting up delegate.
Class MyRequestController:NSObject,NSURLSessionTaskDelegate {
Let session: NSURLSession?
Init(){
Let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
Session = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
}
Deinit{
Session!.invalidateAndCancel()
}
/ / Handle the redirect request, use nil directly to cancel the redirect request
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
/ / Because the interception 302, set the completionHandler parameter to nil, so ignore the redirect request, the Response returned here is the Response containing the 302 status code.
Let resp:NSHTTPURLResponse = response as! NSHTTPURLResponse
Println("Response Header field containing 302 state: \(resp.allHeaderFields)") })
Task.resume()
}
}
So far, by setting a delegate for nsurlconnection or nsurlsession, we can intercept (in fact, return nil) by a callback method.
But in a project where we usually use the third library of Alamofire to operate the network request, would it be troublesome for me to re-write a request myself?
fourth. Perfecting the Alamofire library to achieve interception of 302 requests
Alamofire Nothing to say, analysis of its code can be found, is the use of nsurlsession to implement the request.
That being the case, then we'll find Nsurlsession, set delegate for it, and rewrite willperformhttpredirection.
In the Alamofire.swift file, the request method is exposed to the Sharedinstance property of the manager class that we call to manage its own objects.
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)
}
The implementation of the Manager.sharedinstance property, which defines the request header information, and then calls the constructor
public static let sharedInstance: Manager = {
let configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = Manager.defaultHTTPHeaders
return Manager(configuration: configuration)
}()
Constructor, the nsurlsession we're looking for is here, and it already has a class (sessiondelegate) to implement the corresponding 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?()
}
}
}
This constructor does not seem to be moving, the key is still the Sessiondelegate class, which implements all the 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)
}
}
Careful observation will find that there is a publicvariable (VAR) taskwillperformhttpredirection, there is an overriding method (Willperformhttpredirection ).
As can be seen from this method, it expects us to pass a custom method to the Taskwillperformhttpredirection variable, and if we assign it, it will run our custom method.
We're going to assign a value to the taskwillperformhttpredirection variable, and the parameter is a method.
Add the following code to the manager class:
public typealias TaskWillRedirectAction = ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse,NSURLRequest) -> NSURLRequest?)
public func setTaskWillRedirectAction(action:TaskWillRedirectAction){
self.delegate.taskWillPerformHTTPRedirection = action
}
The changes to the Alamofire library will do just that!
We need to call the Settaskwillredirectaction method and pass in our custom method before sending the network request.
How to use:
var manager = Manager.sharedInstance
manager.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
//Because the settaskwillredirectaction method above returns nil, when processing the rewriting method of nsurlsessiondatadelegate, the parameter of the completionhandler method is nil, which also implements interception! println(response?.allHeaderFields["Location"])
}
Note that you need to get a manager object from the Sharedinstance property, and then use this object to set the blocking callback method before sending the request.
If you still use Alamofire.request to send a request, it won't work because you re-created the manager class object.
Resources
Https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/RequestChanges.html
Http://stackoverflow.com/questions/1446509/handling-redirects-correctly-with-nsurlconnection
Tips
This article consists of Wp2osc Import, original link: http://devonios.com/intercept-302-request.html
because the Oschina OPENAPI automatically filters the IMG tag when processing the content parameter, the picture cannot be displayed, See .
Several ways iOS intercepts redirect requests (302)