Javascript與UIWebView交互,通常的做法是雙方約定一個協議,如:protocol://function/params,在Javascript中用window.location.href = "protocol://function/params";來請求,然後UIWebview的delegate方法:- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType會攔截到這個請求,分析出所要進行的操作。但是通過window.location.href這種方式實際上是GET的方式(請不要計較說GET和POST是http://纔有的請求方式- -),傳輸的數據量是有一定限制的。當數據量很大時,假如達到了5M,是不是可以用POST方式呢,我們知道POST是沒有限制的。
在Foundation的URL加載系統中有一個NSURLProtocol類,它是一個抽象類,可以通過子類化來定義新的或已存在的url加載行爲。當它的子類註冊到url加載系統中時,便可攔截所有url請求。是你想要的請求你可以對其處理,不是的話就不管,交給系統中其他的NSURLProtocol子類處理。那麼js發出的這個post請求,自然可以被你攔截到啦,你要傳的大數據就在request的body裏啦,哈哈~。注意!以post的方式請求,url的scheme必須是http://,可不能是那個什麼protocol://啦。
那麼問題的關鍵就是這個NSURLProtocol的子類究竟怎麼實現了,創建一個AppProtocolHandler類繼承NSURLProtocol,具體的實現如下:
AppProtocolHandler.m
#import "AppProtocolHandler.h"
static NSString * const MyURLProtocolHandledKey = @"MyURLProtocolHandledKey";
@interface AppProtocolHandler()
@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSURLResponse *response;
@end
@implementation AppProtocolHandler
//註冊自己的urlprotocl到url加載系統中
+ (void)registerSpecialProtocol {
static BOOL inited = NO;
if (!inited) {
inited = YES;
[NSURLProtocol registerClass:[AppProtocolHandler class]];
}
}
- (void)handelRequest
{
id<NSURLProtocolClient> client = [self client];
NSURLRequest *request = [self request];
NSURL *url = [request URL];
//我們想要的數據
NSData *bodyData = [request HTTPBody];
NSString* bodyStr = [[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding];
NSLog(@"[request HTTPBody]: %@", bodyStr);
//響應一個空的data回去
NSData *data = [NSData data];
NSURLCacheStoragePolicy caching = NSURLCacheStorageNotAllowed;
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url MIMEType:@"text/plain" expectedContentLength:[data length] textEncodingName:@"utf-8"];
[client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:caching];
[client URLProtocol:self didLoadData:data];
[client URLProtocolDidFinishLoading:self];
}
#pragma mark - Override NSURLProtocol Methods
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b
{
return [super requestIsCacheEquivalent:a toRequest:b];
}
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
if ([NSURLProtocol propertyForKey:MyURLProtocolHandledKey inRequest:request]) {
return NO;
}
return YES;
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
- (void)startLoading
{
id<NSURLProtocolClient> client = [self client];
NSURLRequest *request = [self request];
NSURL *url = [request URL];
//是js給傳值的請求
if ([[url absoluteString]hasPrefix:@"http://test"]) {
[self handelRequest];
return;
}
//是本地文件
if ([[url absoluteString]hasPrefix:@"file://"]) {
//直接讀取本地文件內容,作爲response的內容
NSData *data = [[NSData alloc]initWithContentsOfFile:[url path]];
NSURLCacheStoragePolicy caching = NSURLCacheStorageAllowedInMemoryOnly;
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:url MIMEType:[[url path]pathExtension] expectedContentLength:[data length] textEncodingName:@"utf-8"];
[client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:caching];
[client URLProtocol:self didLoadData:data];
[client URLProtocolDidFinishLoading:self];
}
//服務器上的文件
else{
//重新創建一個request
NSMutableURLRequest *newRequest = [self.request mutableCopy];
//標記是否創建過了該請求,不加標識會陷入一個死循環中
[NSURLProtocol setProperty:@YES forKey:MyURLProtocolHandledKey inRequest:newRequest];
//用nsurlconnection進行網絡請求
self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}
}
- (void) stopLoading {
[self.connection cancel];
self.mutableData = nil;
}
#pragma mark - NSURLConnectionDelegate
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
self.response = response;
self.mutableData = [[NSMutableData alloc] init];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
[self.mutableData appendData:data];
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
@end
在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中添加
[AppProtocolHandlerregisterSpecialProtocol]; 這樣所有的請求都會先讓AppProtocolHandler過濾一遍的。
好了,關鍵的代碼完成了,找一個viewcontroller添加一個uiwebview上去吧,然後加載測試的html:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webview loadRequest:request];
}
怕小夥伴們oc寫久了,忘記了ajax怎麼post請求,再附加一段可測試的html代碼吧(也還是別人幫我寫的):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="http://upcdn.b0.upaiyun.com/libs/jquery/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){
alert(1);
$('#btn').click(function(){
alert('Pre Run Post Request');
$.post("http://test/setTitle", {
"key":"value"
}, function(response){
alert('Return Http Status OK');
alert('Here the response: ' + response);
});
})
});
</script>
</head>
<body>
<button type="button" id="btn">Click Me</button>
</body>
</html>
注:js裏不會報錯的,因爲這個post根本沒走網絡,我就給了它一個長度爲0的data作爲響應。
參考鏈接:
- http://stackoverflow.com/questions/3991360/passing-data-to-objective-c-with-post-rather-than-get
- https://github.com/meeech/2.0.1.GA2-iOS-Classes/blob/master/XHRBridge.m
- http://nshipster.cn/nsurlprotocol/
- http://www.raywenderlich.com/59982/nsurlprotocol-tutorial