日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS 开发中使用 NSURLProtocol 拦截 HTTP 请求

發布時間:2025/7/14 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS 开发中使用 NSURLProtocol 拦截 HTTP 请求 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

?

這篇文章會提供一種在 Cocoa 層攔截所有 HTTP 請求的方法,其實標題已經說明了攔截 HTTP 請求需要的了解的就是 NSURLProtocol。

?

由于文章的內容較長,會分成兩部分,這篇文章介紹 NSURLProtocol 攔截 HTTP 請求的原理,另一篇文章如何進行 HTTP Mock 介紹這個原理在 OHHTTPStubs 中的應用,它是如何 Mock(偽造)某個 HTTP 請求對應的響應的。

?

NSURLProtocol

?

NSURLProtocol 是蘋果為我們提供的 URL Loading System 的一部分,這是一張從官方文檔貼過來的圖片:

?

?

URL-loading-syste

?

官方文檔對 NSURLProtocol 的描述是這樣的:

?

An NSURLProtocol object handles the loading of protocol-specific URL data. The NSURLProtocol class itself is an abstract class that provides the infrastructure for processing URLs with a specific URL scheme. You create subclasses for any custom protocols or URL schemes that your app supports.

?

在每一個 HTTP 請求開始時,URL 加載系統創建一個合適的 NSURLProtocol 對象處理對應的 URL 請求,而我們需要做的就是寫一個繼承自 NSURLProtocol 的類,并通過 - registerClass: 方法注冊我們的協議類,然后 URL 加載系統就會在請求發出時使用我們創建的協議對象對該請求進行處理。

?

這樣,我們需要解決的核心問題就變成了如何使用 NSURLProtocol 來處理所有的網絡請求,這里使用蘋果官方文檔中的 CustomHTTPProtocol 進行介紹,你可以點擊這里下載源代碼。

https://developer.apple.com/library/ios/samplecode/CustomHTTPProtocol/CustomHTTPProtocol.zip

?

在這個工程中 CustomHTTPProtocol.m 是需要重點關注的文件,CustomHTTPProtocol 就是 NSURLProtocol 的子類:

?

@interface CustomHTTPProtocol : NSURLProtocol

?

...

?

@end

?

現在重新回到需要解決的問題,也就是 如何使用 NSURLProtocol 攔截 HTTP 請求?,有這個么幾個問題需要去解決:

?

  • 如何決定哪些請求需要當前協議對象處理?

  • 對當前的請求對象需要進行哪些處理?

  • NSURLProtocol 如何實例化?

  • 如何發出 HTTP 請求并且將響應傳遞給調用者?

?

上面的這幾個問題其實都可以通過 NSURLProtocol 為我們提供的 API 來解決,決定請求是否需要當前協議對象處理的方法是:+ canInitWithRequest:

?

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {

????BOOL shouldAccept;

????NSURL *url;

????NSString *scheme;

?

????shouldAccept = (request != nil);

????if (shouldAccept) {

????????url = [request URL];

????????shouldAccept = (url != nil);

????}

????return shouldAccept;

}

?

因為項目中的這個方法是大約有 60 多行,在這里只粘貼了其中的一部分,只為了說明該方法的作用:每一次請求都會有一個 NSURLRequest 實例,上述方法會拿到所有的請求對象,我們就可以根據對應的請求選擇是否處理該對象;而上面的代碼只會處理所有 URL 不為空的請求。

?

請求經過 + canInitWithRequest: 方法過濾之后,我們得到了所有要處理的請求,接下來需要對請求進行一定的操作,而這都會在 + canonicalRequestForRequest: 中進行,雖然它與 + canInitWithRequest: 方法傳入的 request 對象都是一個,但是最好不要在 + canInitWithRequest: 中操作對象,可能會有語義上的問題;所以,我們需要覆寫 + canonicalRequestForRequest: 方法提供一個標準的請求對象:

?

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {

????return request;

}

?

這里對請求不做任何修改,直接返回,當然你也可以給這個請求加個 header,只要最后返回一個 NSURLRequest 對象就可以。

?

在得到了需要的請求對象之后,就可以初始化一個 NSURLProtocol 對象了:

?

- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id )client {

????return [super initWithRequest:request cachedResponse:cachedResponse client:client];

}

?

在這里直接調用 super 的指定構造器方法,實例化一個對象,然后就進入了發送網絡請求,獲取數據并返回的階段了:

?

- (void)startLoading {

????NSURLSession *session = [NSURLSession sessionWithConfiguration:[[NSURLSessionConfiguration alloc] init] delegate:self delegateQueue:nil];

????NSURLSessionDataTask *task = [session dataTaskWithRequest:self.request];

????[task resume];

}

?

這里使用簡化了 CustomHTTPClient 中的項目代碼,可以達到幾乎相同的效果。

?

你可以在 - startLoading 中使用任何方法來對協議對象持有的 request 進行轉發,包括 NSURLSession、 NSURLConnection 甚至使用 AFNetworking 等網絡庫,只要你能在回調方法中把數據傳回 client,幫助其正確渲染就可以,比如這樣:

?

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {

????[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];

?

????completionHandler(NSURLSessionResponseAllow);

}

?

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {

????[[self client] URLProtocol:self didLoadData:data];

}

?

當然這里省略后的代碼只會保證大多數情況下的正確執行,只是給你一個對獲取響應數據粗略的認知,如果你需要更加詳細的代碼,我覺得最好還是查看一下?CustomHTTPProtocol?中對 HTTP 響應處理的代碼,也就是?NSURLSessionDelegate?協議實現的部分。

?

client 你可以理解為當前網絡請求的發起者,所有的 client 都實現了 NSURLProtocolClient 協議,協議的作用就是在 HTTP 請求發出以及接受響應時向其它對象傳輸數據:

?

@protocol NSURLProtocolClient

...

- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;

?

- (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;

?

- (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;

...

@end

?

當然這個協議中還有很多其他的方法,比如 HTTPS 驗證、重定向以及響應緩存相關的方法,你需要在合適的時候調用這些代理方法,對信息進行傳遞。

?

如果你只是繼承了 NSURLProtocol 并且實現了上述方法,依然不能達到預期的效果,完成對 HTTP 請求的攔截,你還需要在 URL 加載系統中注冊當前類:

?

[NSURLProtocol registerClass:self];

?

需要注意的是?NSURLProtocol?只能攔截?UIURLConnection、NSURLSession?和UIWebView?中的請求,對于?WKWebView?中發出的網絡請求也無能為力,如果真的要攔截來自?WKWebView?中的請求,還是需要實現?WKWebView?對應的?WKNavigationDelegate,并在代理方法中獲取請求。
無論是?NSURLProtocol、NSURLConnection?還是?NSURLSession?都會走底層的 socket,但是?WKWebView?可能由于基于 WebKit,并不會執行 C socket 相關的函數對 HTTP 請求進行處理,具體會執行什么代碼暫時不是很清楚,如果對此有興趣的讀者,可以聯系筆者一起討論。

?

總結

?

如果你只想了解如何對 HTTP 請求進行攔截,其實看到這里就可以了,不過如果你想應用文章中的內容或者希望了解如何偽造 HTTP 響應,可以看下一篇文章如何進行 HTTP Mock。

?

References

?

+ NSURLProtocol

?

Github Repo:https://github.com/draveness/iOS-Source-Code-Analyze

Follow:?https://github.com/Draveness

Source:?http://draveness.me/intercept

?

總結

以上是生活随笔為你收集整理的iOS 开发中使用 NSURLProtocol 拦截 HTTP 请求的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。