NSURLProtocol

重定义整个URL Loading System。

当你注册自定义NSURLProtocol后,就有机会对所有的请求进行统一的处理,基于这一点它可以让你

  • 自定义请求和响应

  • 提供自定义的全局缓存支持

  • 重定向网络请求

  • 提供HTTP Mocking (方便前期测试)

  • 其他一些全局的网络请求修改需求

  • Provide Custom Responses For Your Network Requests:

It doesn’t matter if you’re making a request using aUIWebView,NSURLConnectionor even using a third-party library (like AFNetworking, MKNetworkKit, your own, etc, as these are all built on top ofNSURLConnection). You can provide a custom response, both for metadata and for data. You might use this if you wanted to stub out the response of a request for testing purposes, for example.

  • Skip Network Activity and Provide Local Data:

Sometimes you may think it’s unnecessary to fire a network request to provide the app whatever data it needs._NSURLProtocol_can set your app up to find data on local storage or in a local database.

  • Redirect Your Network Requests:

Have you ever wished you could redirect requests to a proxy server — without trusting the user to follow specific iOS setup directions? Well, you can!_NSURLProtocol_gives you what you want — control over requests. You can set up your app to intercept and redirect them to another server or proxy, or wherever you want to. Talk about control!!

  • Change the User-agent of Your Requests:

Before firing any network request, you can decide to change its metadata or data. For instance, you may want to change the user-agent. This could be useful if your server changes content based on the user-agent. An example of this would be differences between the content returned for mobile versus desktop, or the client’s language.

  • Use Your Own Networking Protocol:

You may have your own networking protocol (for instance, something built on top of UDP). You can implement it and, in your application, you still can can keep using any networking library you prefer.

Needless to say, the possibilities are many. It would be impractical (but not impossible) to list all the possibilities you have withNSURLProtocolin this tutorial. You can do anything you need with a givenNSURLRequestbefore it’s fired by changing the designated NSURLResponse. Better yet, just create your ownNSURLResponse. You’re the developer, after all.

WhileNSURLProtocolis powerful, remember that it’s not a networking library. It’s a tool you can usein additionto the library you already use. In short, you can take advantage ofNSURLProtocol‘s benefits while you use your own library.

Right, now on to some code…

Usage

  • 继承NSURLPorotocl,并注册你的NSURLProtocol

    [NSURLProtocol registerClass:[YXURLProtocol class]];

  • 实现NSURLProtocol的相关方法

    • 当遍历到我们自定义的NSURLProtocol时,系统先会调用canInitWithRequest:这个方法。顾名思义,这是整个流程的入口,只有这个方法返回YES我们才能够继续后续的处理。我们可以在这个方法的实现里面进行请求的过滤,筛选出需要进行处理的请求。

    • 当筛选出需要处理的请求后,就可以进行后续的处理,需要至少实现如下4个方法

      • ·canonicalRequestForRequest: 返回规范化后的request,一般就只是返回当前request即可。

      • ·requestIsCacheEquivalent:toRequest: 用于判断你的自定义reqeust是否相同,这里返回默认实现即可。它的主要应用场景是某些直接使用缓存而非再次请求网络的地方.

      • ·startLoading和stopLoading 实现请求和取消流程。

Problems

Q1 Squashing the Infinite Loop with Tags

Think again about the URL Loading System and protocol registration, and you might have a notion about why this is happening. When the UIWebView wants to load the URL, the URL Loading System asks MyURLProtocol if it can handle that specific request. Your class saysYES, it can handle it.

So the URL Loading System will create an instance of your protocol and callstartLoading. Your implementation then creates and fires its NSURLConnection. But this also calls the URL Loading System. Guess what? Since you’re always returningYESin the+canInitWithRequest:method, it creates another MyURLProtocol instance.

This new instance will lead to a creation of one more, and then one more and then an ifinite number of instances. That’s why you app doesn’t load anything! It just keeps allocating more memory, and shows only one URL in the console. The poor browser is stuck in an infinite loop! Your users could be frustrated to the point of inflicting damage on their devices.

Review what you’ve done and then move on to how you can fix it. Obviously you can’t just always returnYESin the+canInitWithRequest:method. You need to have some sort of control to tell the URL Loading System to handle that request only once. The solution is in theNSURLProtocolinterface. Look for the class method called+setProperty:forKey:inRequest:that allows you to add custom properties to a given URL request. This way, you can ‘tag’ it by attaching a property to it, and the browser will know if it’s already seen it before.

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    static NSUInteger requestCount = 0;
    NSLog(@"Request #%u: URL = %@", requestCount++, request);

    if ([NSURLProtocol propertyForKey:@"MyURLProtocolHandledKey" inRequest:request]) {
        return NO;
    }

    return YES;
}

- (void)startLoading {
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:@"MyURLProtocolHandledKey" inRequest:newRequest];

    self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}

Now the-startLoadingmethod sets a NSNumber instance () for a given key

坑1:企图在canonicalRequestForRequest:进行request的自定义操作,导致各种递归调用导致连接超时。这个API的表述其实很暧昧:

It is up to each concrete protocol implementation to define what “canonical” means. A protocol should guarantee that the same input request always yields the same canonical form.

所谓的canonical form到底是什么呢?而围观了包括NSEtcHosts和RNCachingURLProtocol在内的实现,它们都是直接返回当前request。在这个方法内进行request的修改非常容易导致递归调用(即使通过setProperty:forKey:inRequest:对请求打了标记)

·坑2:没有实现足够的回调方法导致各种奇葩问题。

如connection:willSendRequest:redirectResponse: 内如果没有通过[self client]回传消息,那么需要重定向的网页就会出现问题:host不对或者造成跨域调用导致资源无法加载。

NSURLProtocol cannot intercept WebKit request

WKWebViewmakes requests and renders content out-of-process, meaning your app does not hear the requests they make. If you are missing a functionality, now is the time to open a bug report and/or an enhancement request with Apple.

As of iOS 10.3 SDK,WKWebViewis still unable to make use of customNSURLProtocols using public APIs.

Enterprising developers have found an interesting method:+[WKBrowsingContextController registerSchemeForCustomProtocol:]It supposedly adds the provided scheme to a list of custom protocol handled schemes and should then work withNSURLProtocol.

results matching ""

    No results matching ""