3

我有一个 UIWebView 和一个自定义 NSURLProtocol 类注册到代理 HTTP 请求。我在加载 github.com 时遇到问题。如果我浏览到https://github.com,那么它会很好地加载页面及其内容。但是,如果我浏览到http://github.com,它会正确加载 HTML,但不会加载图像或 CSS。这是我加载 https 版本时的样子:

HTTPS GitHub

这是我加载 http 版本时的样子:

HTTP GitHub

这是我用来重现此问题的视图控制器的代码:

@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获取页面数据的时候不请求网页的内容呢?

4

3 回答 3

5

我们做了同样的工作——重定向有效,但内容没有加载。

Apple 文档之后,他们提到应该使用 NSURLProtocol 子类中的 connection: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;

}

于 2015-03-02T14:01:56.983 回答
4

我最终想通了。当我在 connection:didReceiveResponse: 中收到重定向响应 (HTTP 3xx) 时,我不得不调用 [self.client URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response]。

于 2015-02-18T18:09:12.613 回答
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)
}

已确认它有效。图像和资产正确加载。

于 2017-03-20T11:14:16.220 回答