前言
最近app需要重新整理webview加載的架構(gòu),把webview的請求方式由post改為get漱抓,而之前加載時傳參是通過post的請求體實現(xiàn)度苔,而現(xiàn)在需要將參數(shù)通過請求頭傳遞间狂,而且保證每次加載都要在請求頭加參惨奕。
過程
想要在請求頭加參其實很簡單揖赴,只要通過以下代碼:
[request addValue:"head" forHTTPHeaderField:@"key"];
現(xiàn)在主要問題是要在每次加載都在請求頭加參馆匿,于是我在網(wǎng)上搜到這篇文章 UIWebView 設置請求頭,基本上可以解決我的需求燥滑。下面分析以下代碼:
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
//先判斷是否含有請求頭渐北,打破死循環(huán)
let dic:Dictionary<String,AnyObject> = request.allHTTPHeaderFields!
let token = dic["UserToken"]
if (token != nil) {
return true
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let newUrl = request.URL
let newRequest:NSMutableURLRequest = NSMutableURLRequest.init(URL: newUrl!)
newRequest.addValue(LoginInfoModel.sharedInstance.m_auth, forHTTPHeaderField: "UserToken")
self.webView.loadRequest(newRequest)
})
}
return false
}
如上,在uiwebview的代理方法webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) 中攔截網(wǎng)絡請求铭拧,先檢驗請求中是否含有參數(shù)赃蛛,有參數(shù)即直接通過(這一步十分關鍵,因為攔截的請求如沒有參數(shù)搀菩,會先在請求頭添加參數(shù)呕臂,再讓web view重新loadRequest,并且在代理方法中要返回NO肪跋,loadRequest會重新走這個代理方法歧蒋,如果沒有以上檢測通過return YES的話,就會陷入死循環(huán)州既,)谜洽。另外,之所以要在主線程中操作吴叶,我覺得是與webview的底層加載有關阐虚,webview加載時應該是開了子線程,所以重新加載要在主線程操作蚌卤,保證線程安全敌呈。至于文章中提及這種做法進入有iFrame的的頁面會有bug,由于我們后臺頁面沒有iFrame造寝,因此忽略掉磕洪。
另外還搜到另一種做法 NSURLProtocol學習筆記-UIWebView 設置請求頭,這種做法聽說更加完美地避免bug诫龙,我需要再驗證下析显,后續(xù)再更新......
通過NSURLProtocol修改請求頭
如上,網(wǎng)上搜到另外一種被推薦的做法签赃,利用NSURLProtocol修改請求頭谷异。可以先通過這篇文章 大致了解NSURLProtocol,實際操作時可參考這兩篇(一锦聊,二)歹嘹。以上兩篇文章在攔截請求后分別用NSURLConnection和NSURLSession,但由于NSURLConnection在iOS9中已經(jīng)停用孔庭,所以我參考的是第二篇文章尺上,以下對代碼進行分析材蛛。
首先如上面的文章所說,NSURLProtocol是一個抽象類怎抛,不能直接使用卑吭,需要子類化使用。建一個繼承自NSURLProtocol的子類
import UIKit
class MyURLProtocol: NSURLProtocol ,NSURLSessionDataDelegate {
var session : NSURLSession?
//判斷是否攔截
override class func canInitWithRequest(request: NSURLRequest) -> Bool {
return true
}
//修改攔截的請求
override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest{
let newRequest : NSMutableURLRequest = request.mutableCopy() as! NSMutableURLRequest
newRequest.addValue("111", forHTTPHeaderField: "ttt")
return newRequest;
}
//執(zhí)行特定的request請求
override func startLoading() {
let request = self.request.mutableCopy()
NSURLProtocol.setProperty((true), forKey: "SessionProtocolKey", inRequest: request as! NSMutableURLRequest)
let config : NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
self.session = NSURLSession.init(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
let task : NSURLSessionDataTask = self.session!.dataTaskWithRequest(request as! NSURLRequest)
task.resume()
}
//取消特定的request請求
override func stopLoading() {
self.session!.invalidateAndCancel()
self.session = nil
}
//MARK: - NSURLSessionDataDelegate
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if (error != nil){
self.client?.URLProtocol(self, didFailWithError: error!)
}else{
self.client?.URLProtocolDidFinishLoading(self)
}
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
self.client?.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: NSURLCacheStoragePolicy.NotAllowed)
completionHandler(NSURLSessionResponseDisposition.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.client?.URLProtocol(self, didLoadData: data)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: (NSCachedURLResponse?) -> Void) {
completionHandler(proposedResponse)
}
}
在子類中需實現(xiàn)4各方法
1.func canInitWithRequest(request: NSURLRequest) -> Bool//在此方法決定是否攔截請求马绝,return yes為攔截
2.func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest//在此方法修改請求并返回
3.func startLoading()//執(zhí)行特定的request請求
4.func stopLoading()//中斷特定的request請求
之后就是實現(xiàn)NSURLSession的代理方法(這里我不太了解豆赏,都是照著上面文章的代碼去寫)。
接下來還有做一步富稻,因為我們只是要修改webView的請求掷邦,所以我們可以在webview的VC的viewDidLoad方法中向NSURLProtocol注冊我們寫的子類
//注冊URLProtocol
NSURLProtocol.registerClass(MyURLProtocol)
當然記得在deinit移除子類(deinit方法在swift中相當于OC中的dealloc)
deinit{
NSURLProtocol.unregisterClass(MyURLProtocol)
}
當然,如果我們想整個app的網(wǎng)絡請求都要修改的話椭赋,那我們就可以在application didFinishLaunchingWithOptions的方法里注冊我們的子類對象耙饰,就可以愉快地在整個app的網(wǎng)絡請求中為所欲為了,哈哈哈纹份。哦苟跪,放上我的demo,就這樣吧蔓涧。
結(jié)束
最近一直在思考件已,iOS程序員未來應該如何發(fā)展,內(nèi)心一直十分困惑元暴,望與諸君交流中解惑篷扩。學習之路,與君共勉茉盏。