目前我正在开发一个混合应用程序,该应用程序使用webView shouldStartLoadWithRequest:为登录提供令牌。我的功能适用于我提出的每一个正常请求(例如点击)
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request
NSLog([NSString stringWithFormat:@"Loading View: %@",[[request URL] absoluteString]]);
if ([[[request URL] absoluteString] rangeOfString:BASE_URL].location != NSNotFound) {
NSString *token = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginToken];
NSString *hash = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginHash];
NSString *params = [NSString stringWithFormat:@"mobile=app&user_token=%@&user_hash=%@",token,hash];
if([[request URL] query] == nil) {
[self LoadUrl:[[request URL] absoluteString] withGetParams:params append:NO];
return NO;
}else{
if([[[request URL] absoluteString] rangeOfString:params].location == NSNotFound){
[self LoadUrl:[[request URL] absoluteString] withGetParams:params append:YES];
return NO;
}
}
}
-(void)LoadUrl:(NSString *)url withGetParams:(NSString *)params append:(BOOL)append{
NSString *PreUrl;
if(append == YES) PreUrl = [NSString stringWithFormat:@"%@&%@",url,params];
else PreUrl = [NSString stringWithFormat:@"%@?%@",url,params];
NSURL *nsurl = [NSURL URLWithString: PreUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:nsurl];
[self.WebView loadRequest:request];
}
我对这个代码的问题是,如果我加载一个图像,例如,它将被检测为"附加哈希"(这是正确的,我希望每个请求都包含Auth),但图像将被加载到Webview本身中。
我的第一次尝试(在切换到这个模型之前)是修改解析的请求。但每一个变化都被忽视了。。。。
有人知道我该怎么解决这个问题吗?真的有办法修改请求吗?如果没有,我至少可以确定请求的"目标"还是转发它?
感谢您的帮助
我为我的问题找到了解决方案。Sublcassing是正确的方法,但不是UIWebView,而是自己的NSURLProtocol。
所以我做了什么:
创建自己的NSURL协议子类
@interface MyURL : NSURLProtocol <NSURLConnectionDelegate>
为HTTP连接添加一些标准处理
@interface MyURL () <NSURLConnectionDelegate>
@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSURLResponse *response;
@end
@implementation MyURL
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
- (void)stopLoading
{
[self.connection cancel];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.client URLProtocol:self didLoadData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self.client URLProtocol:self didFailWithError:error];
self.connection = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self.client URLProtocolDidFinishLoading:self];
self.connection = nil;
}
@end
现在有趣的部分是——修改进入我的服务器的每个请求
所以首先:检查这个请求是否到达我的服务器,并确定我的协议是否应该处理它
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
NSString *token = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginToken];
NSString *hash = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginHash];
if([NSURLProtocol propertyForKey:@"TokenSet" inRequest:request]) return NO; // We already handled it
if((hash == nil) || (token == nil) ) return NO; // We are not logged in
NSString *params = [NSString stringWithFormat:@"mobile=app&user_token=%@&user_hash=%@",token,hash];
if (([[[request URL] absoluteString] rangeOfString:BASE_URL].location != NSNotFound) && ([[[request URL] absoluteString] rangeOfString:@"/assets/"].location == NSNotFound)){
if([[[request URL] absoluteString] rangeOfString:params].location == NSNotFound){
return YES; // URL does not contain the login token & we're not requesting an asset (js/img/etc.)
}
}
return NO;
}
因此,如果+(BOOL)canInitWithRequest:(NSURLRequest*)请求返回yes,我必须处理该请求。我已经知道它不包含登录令牌&hash,所以我必须确定是否必须附加它。一般来说,为了修改请求,我创建了一个请求的MutableCopy,对其进行修改,并将URLConnection设置为该请求。
- (void)startLoading
{
NSMutableURLRequest *newRequest = [self.request mutableCopy];
NSString *PreURL;
NSString *token = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginToken];
NSString *hash = [[NSUserDefaults standardUserDefaults] stringForKey:kDefaultsKeyLoginHash];
NSString *params = [NSString stringWithFormat:@"mobile=app&user_token=%@&user_hash=%@",token,hash];
if([[newRequest URL] query] == nil) {
PreURL = [NSString stringWithFormat:@"%@?%@",[[newRequest URL] absoluteString],params];
}else{
if([[[newRequest URL] absoluteString] rangeOfString:params].location == NSNotFound){
PreURL = [NSString stringWithFormat:@"%@&%@",[[newRequest URL] absoluteString],params];
}
}
NSURL *nsurl = [NSURL URLWithString: PreURL];
[newRequest setURL:nsurl];
[NSURLProtocol setProperty:@"YES" forKey:@"TokenSet" inRequest:newRequest];
self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}
为了完成这一切,我们在AppDelegate中将URL协议注册为协议。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[NSURLProtocol registerClass:[MyURL class]];
}
有了这个解决方案,我首先有一个优势,那就是在我的应用程序的任何部分发送到我的服务器的任何请求中都有我的登录令牌。不用再担心了。我可以做一些很酷的事情,比如在第一次加载资源后节省资源,甚至在Webview中使用我的应用程序包中的图像。。。
我希望这能帮助到别人。