我有一个UIWebView和一个自定义的NSURLProtocol类,用于代理HTTP请求。我在加载github.com时遇到问题。如果我浏览到https://github.com,那么它会加载页面及其内容。但是,如果我浏览到http://github.com,它会正确加载HTML,但它不会加载图像或CSS。这是我加载https版本时的样子:
这是我加载http版本时的样子:
这是我用来重现此问题的视图控制器的代码:
@interface ViewController ()
{
UIWebView *aWebView;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[NSURLProtocol registerClass:[WebViewProxyURLProtocol class]];
aWebView = [[UIWebView alloc] initWithFrame:self.view.frame];
aWebView.delegate = self;
[self.view addSubview:aWebView];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://github.com"]];
[aWebView loadRequest:request];
}
@end
这是NSURLProtocol的实现:
@interface WebViewProxyURLProtocol : NSURLProtocol <NSURLConnectionDataDelegate>
@end
@implementation WebViewProxyURLProtocol {
NSMutableURLRequest* correctedRequest;
}
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
return ([[request allHTTPHeaderFields] objectForKey:@"X-WebViewProxy"] == nil);
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
return NO;
}
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client {
if (self = [super initWithRequest:request cachedResponse:cachedResponse client:client]) {
// Add header to prevent loop in proxy
correctedRequest = request.mutableCopy;
[correctedRequest addValue:@"True" forHTTPHeaderField:@"X-WebViewProxy"];
}
return self;
}
- (void)startLoading {
[NSURLConnection connectionWithRequest:correctedRequest delegate:self];
}
- (void)stopLoading {
correctedRequest = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
- (NSURLRequest *)connection:(NSURLConnection *)connection
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
{
return request;
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
return nil;
}
@end
如果我禁用自定义NSURLProtocol类,它可以正常工作。我使用Charles来检查HTTP请求和响应,它们在使用和不使用NSURLProtocol时看起来相同。
所以问题是:为什么UIWebView在使用NSURLConnection获取页面数据时不会请求网页内容?
答案 0 :(得分:5)
我们做了同样的工作 - 重定向工作但内容没有加载。
在Apple doc之后,他们提到重定向应该使用NSURLProtocol子类中的连接:willSendRequest:redirectResponse委托方法来处理。
我们的解决方案最终看起来像这样:
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response {
if (response) {
NSMutableURLRequest *redirect = [request mutableCopy];
[NSURLProtocol removePropertyForKey:kFlagRequestHandled inRequest:redirect];
[RequestHelper addWebViewHeadersToRequest:redirect];
// THE IMPORTANT PART
[self.client URLProtocol:self wasRedirectedToRequest:redirect redirectResponse:response];
return redirect;
}
return request;
}
答案 1 :(得分:4)
我最终想到了这一点。当我在连接中得到重定向响应(HTTP 3xx)时:didReceiveResponse:我不得不调用[self.client URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response]。
答案 2 :(得分:1)
对于那些寻找Swift解决方案的人来说,我已经采用了@micmdk所做的并对其进行了调整。
在我的情况下,我使用URLProtocol
类拦截请求。在override func startLoading()
方法中,我在请求上设置了一个属性:
override func startLoading() {
...
guard let mutableRequest = request as? NSMutableURLRequest else {
exit(0)
}
URLProtocol.setProperty("true", forKey:"requestHasBeenHandled", in: mutableRequest)
return mutableRequest as URLRequest
}
这可以防止重定向被处理。所以我删除了属性,如下:
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {
guard let redirect = request as? NSMutableURLRequest else {
exit(0)
}
URLProtocol.removeProperty(forKey: "requestHasBeenHandled", in: redirect)
self.client?.urlProtocol(self, wasRedirectedTo: redirect as URLRequest, redirectResponse: response)
}
确认它有效。图像和资产正确加载。