关于WKWebView的post请求丢失body问题的处理方案
WKWebView的优点这里不做过多详情,主要说一下最近处理WKWebView的post请求丢失body问题的处理方案。WKWebView 通过loadrequest方法加载Post请求会丢失请求体(body)中的内容,进而导致服务器拿不到body中的内容的问题的发生。这个问题的产生主要是由于WKWebView的网络请求的进程与APP不是同一个进程,所以网络请求的过程是这样的:
由APP所在的进程发起request,而后通过IPC通信(进程间通信)将请求的相关信息(请求头、请求行、请求体等)传递给webkit网络线进程接收包装,进行数据的HTTP请求,最终再进行IPC的通信回传给APP所在的进程的。这里假如发起的request请求是post请求的话,因为要进行IPC数据传递,传递的请求体body中根据系统调度,将其舍弃,最终在WKWebView网络进程接受的时候请求体body中的内容变成了空,导致此种情况下的服务器获取不到请求体,导致问题的产生。
为了能够获取POST方法请求之后的body内容,这两天整理了少量处理方案,大致分为三种:
1.将网络请求交由Js发起,绕开系统
WKWebView的网络的进程请求达到正常请求的目的
2.改变POST请求的方法为GET方法(有风险,不肯定服务器会接受GET方法)
3.将Post请求的请求body内容放入请求的Header中,并通过URLProtocol阻拦自己设置协议,在阻拦中通过NSConnection进行重新请求(重新包装请求body),而后通过回调Client用户端来传递数据内容
三种方法中,我采用了第三种方案,这里说一下第三种方案的实现方式,大致分为三步:
1.注册阻拦的自己设置的scheme
2.重写loadRequest()方法,根据request的method方法能否为POST进行URL的阻拦替换
3.在URLProtocol中进行request的重新包装(获取请求的body内容),使用NSURLConnection进行HTTP请求并将数据回传
这里说明一下为什么要自己去注册自己设置的scheme,而不是直接阻拦https/http。主要起因是:假如注册了https/http的阻拦,那么所有的http(s)请求都会交由系统进程解决,那么此时系统进程会通过IPC的形式传递给实现URLProctol协议的类去解决,在通过IPC传递的过程中丢失body体(上面有讲到),所以在阻拦的时候是拿不到POST方法的请求体body的。然而并不是所有的http请求都会走loadrequest()方法(比方js中的ajax请求),所以导致少量POST请求没有被包装(将请求体body内容放到请求头header)就被阻拦了,进而丢失请求体body内容,问题一样会产生。所以为了避免这样的问题,我们需要自己去定一个scheme协议,保证不过度阻拦并且能够解决我们需要解决的POST请求内容。
以下是具体的实现方式:
1.注册阻拦的自己设置的
scheme
[NSURLProtocol registerClass:NSClassFromString(@“GCURLProtocol")]; [NSURLProtocol wk_registerScheme:@"gc"]; [NSURLProtocol wk_registerScheme:WkCustomHttp]; [NSURLProtocol wk_registerScheme:WkCustomHttps];2.重写
loadRequest()方法,根据request的method方法能否为POST进行URL的阻拦替换
//包装请求头内容- (WKNavigation *)loadRequest:(NSURLRequest *)request{ NSLog(@"发起请求:%@ method:%@",request.URL.absoluteString,request.HTTPMethod); NSMutableURLRequest *mutableRequest = [request mutableCopy]; NSMutableDictionary *requestHeaders = [request.allHTTPHeaderFields mutableCopy]; //判断能否是POST请求,POST请求需要包装request中的body内容到请求头中(会有丢失body问题的产生) //,包装完成之后重定向到阻拦的协议中自己包装解决请求数据内容,阻拦协议是GCURLProtocol,请自行搜索 if ([mutableRequest.HTTPMethod isEqualToString:@"POST"] && ([mutableRequest.URL.scheme isEqualToString:@"http"] || [mutableRequest.URL.scheme isEqualToString:@"https"])) { NSString *absoluteStr = mutableRequest.URL.absoluteString; if ([[absoluteStr substringWithRange:NSMakeRange(absoluteStr.length-1, 1)] isEqualToString:@"/"]) { absoluteStr = [absoluteStr stringByReplacingCharactersInRange:NSMakeRange(absoluteStr.length-1, 1) withString:@""]; } if ([mutableRequest.URL.scheme isEqualToString:@"https"]) { absoluteStr = [absoluteStr stringByReplacingOccurrencesOfString:@"https" withString:WkCustomHttps]; }else{ absoluteStr = [absoluteStr stringByReplacingOccurrencesOfString:@"http" withString:WkCustomHttp]; } mutableRequest.URL = [NSURL URLWithString:absoluteStr]; NSString *bodyDataStr = [[NSString alloc]initWithData:mutableRequest.HTTPBody encoding:NSUTF8StringEncoding]; [requestHeaders addEntriesFromDictionary:@{@"httpbody":bodyDataStr}]; mutableRequest.allHTTPHeaderFields = requestHeaders; NSLog(@"当前请求为POST请求Header:%@",mutableRequest.allHTTPHeaderFields); } return [super loadRequest:mutableRequest];}3.在
URLProtocol中进行request的重新包装(获取请求的body内容),使用NSURLConnection进行HTTP请求并将数据回传(以下是主要代码)
+ (BOOL)canInitWithRequest:(NSURLRequest *)request { NSString *scheme = request.URL.scheme; if ([scheme isEqualToString:InterceptionSchemeKey]){ if ([self propertyForKey:HaveDealRequest inRequest:request]) { NSLog(@"已经解决,放行"); return NO; } return YES; } if ([scheme isEqualToString:WkCustomHttp]){ if ([self propertyForKey:HaveDealWkHttpPostBody inRequest:request]) { NSLog(@"已经解决,放行"); return NO; } return YES; } if ([scheme isEqualToString:WkCustomHttps]){ if ([self propertyForKey:HaveDealWkHttpsPostBody inRequest:request]) { NSLog(@"已经解决,放行"); return NO; } return YES; } return NO; }- (void)startLoading { //截获 gc 链接的所有请求,替换成本地资源或者者线上资源 if ([self.request.URL.scheme isEqualToString:InterceptionSchemeKey]) { [self htmlCacheRequstLoad]; } else if ([self.request.URL.scheme isEqualToString:WkCustomHttp] || [self.request.URL.scheme isEqualToString:WkCustomHttps]){ [self postBodyAddLoad]; } else{ NSMutableURLRequest *newRequest = [self cloneRequest:self.request]; NSString *urlString = newRequest.URL.absoluteString; [self addHttpPostBody:newRequest]; [NSURLProtocol setProperty:@YES forKey:GCProtocolKey inRequest:newRequest]; [self sendRequest:newRequest]; } }- (void)addHttpPostBody:(NSMutableURLRequest *)redirectRequest{ //判断当前的请求能否是Post请求 if ([self.request.HTTPMethod isEqualToString:@"POST"]) { NSLog(@"post请求"); NSMutableDictionary *headerDict = [redirectRequest.allHTTPHeaderFields mutableCopy]; NSString *body = headerDict[@"httpbody"]?:@""; if (body.length) { redirectRequest.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding]; NSLog(@"body:%@",body); } }}- (void)postBodyAddLoad{ NSMutableURLRequest *cloneRequest = [self cloneRequest:self.request]; if ([cloneRequest.URL.scheme isEqualToString:WkCustomHttps]) { cloneRequest.URL = [NSURL URLWithString:[cloneRequest.URL.absoluteString stringByReplacingOccurrencesOfString:WkCustomHttps withString:@"https"]]; [NSURLProtocol setProperty:@YES forKey:HaveDealWkHttpsPostBody inRequest:cloneRequest]; }else if ([cloneRequest.URL.scheme isEqualToString:WkCustomHttp]){ cloneRequest.URL = [NSURL URLWithString:[cloneRequest.URL.absoluteString stringByReplacingOccurrencesOfString:WkCustomHttp withString:@"http"]]; [NSURLProtocol setProperty:@YES forKey:HaveDealWkHttpPostBody inRequest:cloneRequest]; } //增加body内容 [self addHttpPostBody:cloneRequest]; NSLog(@"请求body增加完成:%@",[[NSString alloc]initWithData:cloneRequest.HTTPBody encoding:NSUTF8StringEncoding]); [self sendRequest:cloneRequest]; }//复制Request对象- (NSMutableURLRequest *)cloneRequest:(NSURLRequest *)request{ NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:request.URL cachePolicy:request.cachePolicy timeoutInterval:request.timeoutInterval]; newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields; [newRequest setValue:@"image/webp,image/*;q=0.8" forHTTPHeaderField:@"Accept"]; if (request.HTTPMethod) { newRequest.HTTPMethod = request.HTTPMethod; } if (request.HTTPBodyStream) { newRequest.HTTPBodyStream = request.HTTPBodyStream; } if (request.HTTPBody) { newRequest.HTTPBody = request.HTTPBody; } newRequest.HTTPShouldUsePipelining = request.HTTPShouldUsePipelining; newRequest.mainDocumentURL = request.mainDocumentURL; newRequest.networkServiceType = request.networkServiceType; return newRequest;}#pragma mark - NSURLConnectionDataDelegate- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ /** * 收到服务器响应 */ NSURLResponse *returnResponse = response; [self.client URLProtocol:self didReceiveResponse:returnResponse cacheStoragePolicy:NSURLCacheStorageAllowed];}- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ /** * 接收数据 */ if (!self.recData) { self.recData = [NSMutableData new]; } if (data) { [self.recData appendData:data]; }}- (nullable NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(nullable NSURLResponse *)response{ /** * 重定向 */ if (response) { [self.client URLProtocol:self wasRedirectedToRequest:request redirectResponse:response]; } return request;}- (void)connectionDidFinishLoading:(NSURLConnection *)connection{ [self.client URLProtocolDidFinishLoading:self];}- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ /** * 加载失败 */ [self.client URLProtocol:self didFailWithError:error];}1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 关于WKWebView的post请求丢失body问题的处理方案