標籤:orm ict vpd hello 直接 proc catch 自己 json
iOS 使用Ajax實現與Javascript同步非同步互動
實現原理:
1.Ajax可以實現同步與非同步請求2.UIWebView可以實現Ajax跨域請求3.NSURLProtocol可以攔截Ajax請求4.NSURLProtocol可以實現類比響應結果
需要解決的問題:
2.1.實現NSURLProtocol攔截Ajax請求2.實現Ajax跨域,解決Ajax預檢請求問題3.實現NSURLProtocol返迴響應
對於上述問題,我們定義自己的NSURLProtocol
#import <Foundation/Foundation.h>@interface MyURLProtocol : NSURLProtocol@end
代碼實現
我們這裡指定schema為 oschina://
對於其中可能遇到預檢請求問題,請參閱(Ajax跨域(CROS)請求中的Preflighted Requests)
@interface MyURLProtocol() @property(nomatic,strong) NSMutableDictionary * reponseHeader;@end@implementation MyURLProtocol//複寫canInitWithRequest,決定是否攔截請求+(BOOL)canInitWithRequest:(NSURLRequest *)request{ //這裡實現對 oschina://syncHttpRequest和oschina://asyncHttpRequest攔截 if(request.URL.scheme!=nil && [[request.URL.scheme lowercaseString] isEqualToString:@"oschina"]) { if([request.URL.host isEqualToString:@"syncHttpRequest"] || [request.URL.host isEqualToString:@"asyncHttpRequest"]) { if(_reponseHeader==nil) { _reponseHeader = @{ @"Access-Control-Allow-Credentials":@"true", @"Access-Control-Allow-Origin":@"*", @"Access-Control-Expose-Headers":@"jsStr", @"Access-Control-Allow-Methods":@"GET,POST,PUT,OPTIONS,HEAD", @"Access-Control-Allow-Headers":@"Origin,jsStr,Content-Type,X-Request-Width", @"Access-Control-Max-Age":@"10", @"Cache-Control":@"no-cache,private", @"Pragma":@"no-cache,no-store", @"Expires":@"0", @"Connection":@"Close" }; } return YES; } } //如果不攔截,則返回NO return NO;}//複寫 canonicalRequestForRequest ,加工請求,這裡我們可以不加工,直接使用req+ (NSURLRequest*) canonicalRequestForRequest:(NSURLRequest *)req{ return req;}//複寫startLoading,並處理預檢請求- (void) startLoading{ //處理跨網域作業,如果是options操作。如果是跨域訪問會發送一個options請求,需要response一個許可權才會繼續走head請求 //此外,ajax發送的資料無法被接收,需要一個自訂要求標頭X-Javascript-Header,用來javascript->iOS傳遞資料 if ([self.request.HTTPMethod isEqualToString:@"OPTIONS"]) { NSDictionary * fields_resp = _reponseHeader; //響應ajax預檢請求 NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[self.request URL] statusCode:200 HTTPVersion:@"1.1" headerFields:fields_resp]; [[self client] URLProtocol: self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; [[self client] URLProtocol:self didLoadData:[NSData data]]; [[self client] URLProtocolDidFinishLoading:self]; }else{ //實現對ajax正式請求的解析與響應 [self doRequestToResponse]; }}-(void) doRequestToResponse{ NSDictionary *dic = [self.request.allHTTPHeaderFields copy]; NSString *jsStr = dic[@"X-Javascript-Header"]; //擷取回應標頭資料 NSString * userAgentInStorage = [[NSUserDefaults standardUserDefaults] stringForKey:@"UserAgent"]; NSString * userAgent = dic[@"User-Agent"];//必要時儲存user-Agent if([NSString isEmptyOrNil:userAgentInStorage] && ![NSString isEmptyOrNil:userAgent]) { [[NSUserDefaults standardUserDefaults] setObject:userAgent forKey:@"UserAgent"]; [[NSUserDefaults standardUserDefaults] synchronize]; } if([NSString isEmptyOrNil:jsStr]) { [self sendRequestErrorToClient]; return; } if([jsStr hasPrefix:@"@"]) { jsStr = [jsStr stringByReplacingOccurrencesOfString:@"@" withString:@""]; } NSData *data = [GTMBase64 decodeString:jsStr]; jsStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; // 轉換 jsStr = [jsStr stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]; jsStr = [jsStr stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]; jsStr = [jsStr stringByReplacingOccurrencesOfString:@"\t" withString:@"\\t"]; jsStr = [jsStr stringByReplacingOccurrencesOfString:@"\0" withString:@"\\0"]; NSMutableDictionary *jsDic = [jsStr mutableObjectFromJSONString]; if(jsDic==nil) { NSString * tempJsStr = [jsStr stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]; jsDic = [tempJsStr mutableObjectFromJSONString]; } if(jsDic==nil) { [UMJS showToast:@"參數解析失敗!"]; return; } NSString *serviceName= jsDic[@"service"]; NSString *methodName = jsDic[@"method"]; id params = jsDic["params"]; [------------------處理響應的請結果------------------------] //1.開始處理,略 //發送相應資料到Ajax端,假定結果為result NSString * response = [@{@"result":result,@"msg":@"Hello World",@"code":@1} JSONString]; [self sendResponseToClient:response]; [------------------處理響應的請結果------------------------]}-(void) sendResponseToClient:(NSString *) str{ NSData *repData = [str dataUsingEncoding:NSUTF8StringEncoding]; NSMutableDictionary *respHeader = [NSMutableDictionary dictionaryWithDictionary:fields_resp]; respHeader[@"Content-Length"] = [NSString stringWithFormat:@"%ld",repData.length]; NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[self.request URL] statusCode:200 HTTPVersion:@"1.1" headerFields:respHeader]; [[self client] URLProtocol: self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; [[self client] URLProtocol:self didLoadData:repData]; [[self client] URLProtocolDidFinishLoading:self]; }//發送錯誤請求資訊-(void) sendRequestErrorToClient{ NSData *data = [@"" dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary * fields_resp =_reponseHeader; NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[self.request URL] statusCode:400 HTTPVersion:@"1.1" headerFields:fields_resp]; [[self client] URLProtocol: self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; [[self client] URLProtocol:self didLoadData:data]; [[self client] URLProtocolDidFinishLoading:self];}- (void) stopLoading{// NSLog(@"stopLoading");}//處理跳轉(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response { if ([response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response; if ([HTTPResponse statusCode] == 301 || [HTTPResponse statusCode] == 302) { NSMutableURLRequest *mutableRequest = [request mutableCopy]; [mutableRequest setURL:[NSURL URLWithString:[[HTTPResponse allHeaderFields] objectForKey:@”Location”]]]; request = [mutableRequest copy]; [[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response]; } } return request; }
自訂結束之後,我們需要在AppDetegate或者UIViewController註冊一下才行,注意:多次註冊也是可以的,不會造成多次攔截。
[NSURLProtocol registerClass:[UyURLProtocol class]];
通過這種方式,我們可以實現iOS端資料處理,在Javascript端我們需要實現2類調用,同步和非同步
//非同步請求function sendAsyncAjax(xJavascript, onload, onerror) { var xhr, results, url; url = ‘oschina://asyncHttpRequest?rnd=‘+Math.random(); xhr = new XMLHttpRequest(); try{ xhr.open(‘POST‘, url, true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.setRequestHeader("Cache-Control", "no-cache,private"); xhr.setRequestHeader("Pragma", "no-cache,no-store"); xhr.setRequestHeader("User-Agent", navigator.userAgent); //通過X-Javascript-Header發送資料到iOS,注意,使用第三方Base64 encode xhr.setRequestHeader("X-Javascript-Header", Base64Util.encode(xJavascript)); xhr.onload = function (e) { if (this.status === 200) { results = JSON.parse(xhr.responseText); onload(results); }else{ onload({‘e‘:e}); } }; xhr.onerror = function (e) { onerror({‘e‘:e}); }; }catch(exception){ console.error(exception); }finally{ try{ xhr.send(null); }catch(exception2){} }}//同步請求function sendSyncAjax(xJavascript) { var xhr, results, url; url = ‘oschina://syncHttpRequest?rnd=‘+Math.random(); xhr = new XMLHttpRequest(); try{ xhr.open(‘POST‘, url, true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.setRequestHeader("Cache-Control", "no-cache,private"); xhr.setRequestHeader("Pragma", "no-cache,no-store"); xhr.setRequestHeader("User-Agent", navigator.userAgent); //通過X-Javascript-Header發送資料到iOS,注意,使用第三方Base64 encode xhr.setRequestHeader("X-Javascript-Header", Base64Util.encode(xJavascript)); }catch(exception){ console.error(exception); }finally{ try{ xhr.send(null); }catch(exception2){} } if (xhr.readyState == 4) { if (xhr.status == 200) { return xhr.execXhr.responseText; } else { return xhr.execXhr.responseText; } } return {};}
然後我們通過javascript調用
var script = JSON.stringify({service:"NetworkUtil",method:"getNetworkInfo",params:{}};sendAsyncAjax(script ,function(result){}, function(error){});或者var script = JSON.stringify({service:"NetworkUtil",method:"getNetworkInfo",params:{}};var result = sendSyncAjax(script);
一般來說NetworkUtil可以調用的方法必須是類方法
@implementation NetworkUtil+(NSString * )getNetworkInfo{ NSString * result = [略]; return result;}@end
我們這裡實現了iOS平台上同步非同步方法,Android平台有系統提供的javascriptInterface介面,當然,我們想要實現和本篇iOS類似的方式,我們可以使用ServerSocket方式來實現,具體過程可能使用到訊號量的概念,這裡不再贅述.
大家可以加我的或者QQ:1499732272 ,我會提供關於iOS開發相關的各種資料!
iOS WebView使用Ajax與iOS的互動