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