NSURLProtocol 详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NSURLProtocol 详解相关的知识,希望对你有一定的参考价值。

参考技术A

初衷是为了让 UIWebView 可以拦截 Ajax 请求。研究了一番找到了 NSURLProtocol。

NSURLProtocol 是属于 Foundation 框架里的 URL Loading System 的一部分,它是一个抽象类,不能去实例化它,只能子类化NSURLProtocol,然后使用的时候注册子类。一个相对晦涩难解的类。

那么如果开发者自定义的一个 NSURLProtocol 并且注册到 app 中,那么在这个自定义的 NSURLProtocol 中我们可以拦截所有的请求,进行修改,或者修改 response。

NSHipster 上说: 「或者这么说吧: NSURLProtocol 就是一个苹果允许的中间人攻击。」

由于 NSURLProtocol 是一个抽象类,所以使用的时候必须定义一个它的子类:

NSURLConnection 发起请求的时候,会让所有已注册的 URLProtocol 来“审批”这个请求

注意: 如果是基于 NSURLSession 进行的请求,注册的时候需要注册到 NSURLSessionConfiguration 中:

记得用完之后注销:

注册成功之后,就需要我们的子类去实现抽象方法:

用来 审批 的方法。

前面说到「NSURLConnection 发起请求的时候,会让所有已注册的 URLProtocol 来“审批”这个请求」。这里返回NO代表放过这个请求,不作处理。返回YES,代表需要处理,则会进入后续的流程。

注意:这里需要放过已经处理过的请求:

这个方法用来统一处理请求 request 对象的,可以修改头信息,或者重定向。没有特殊需要,则直接return request;

如果要在这里做重定向以及头信息的时候注意检查是否已经添加,因为这个方法可能被调用多次,也可以在后面的方法中做。

判断网络请求是否一致,一致的话使用缓存数据。没需要就调用 super 的方法。

子类中最重要的方法就是 -startLoading 和 -stopLoading,实现请求和取消流程。不同的自定义子类在调用这两个方法是会传入不同的内容,但共同点都是要围绕 protocol 客户端进行操作。

可以在这里修改请求信息,重定向,DNS解析,返回自定义的测试数据。

重点:需要标记已经处理过的 request:

URLProtocolHandledKey 是:

举例:直接在 startLoading 中返回测试数据:

需要注意的是父类中有一个 client 属性。

实现的协议 <NSURLProtocolClient> 如下:

对于需要处理的 connection,可以在下的 NSURLConnectionDataDelegate 中进行操作:

如果我们顺序注册 A B C 三个 Protocol,那么一个 connection 在发送的时候,处理的顺序是 C B A,而且最多只有一个 Protocol 会触发处理。

拦截 UIWebview 的请求,会有被拒的风险。

注意标记处理过的,具体做法在本文搜关键词 URLProtocolHandledKey 。

swift自定义NSURLProtocol错误

【中文标题】swift自定义NSURLProtocol错误【英文标题】:swift custom NSURLProtocol error 【发布时间】:2015-09-26 02:11:25 【问题描述】:

我想加载远程url并使用本地资源(image,css,js...),所以我继承了NSURLProtocol。

import Foundation

class MyURLProtocol: NSURLProtocol 

    override class func canInitWithRequest(request: NSURLRequest) -> Bool 
        print(request.URL!.absoluteString)
        if request.URL!.absoluteString.hasSuffix("jquery.min.js")
            return true
        
        return false
    

    override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest
        let urlPath = NSBundle.mainBundle().pathForResource("jquery.min", ofType: "js", inDirectory:"res/js")
        let url = NSURL.fileURLWithPath(urlPath!)
        let mutableReqeust = request.mutableCopy() as! NSMutableURLRequest
        mutableReqeust.URL = url
        return mutableReqeust
    

在 AppDelegate 中注册新的 url 协议

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool 
    NSURLProtocol.registerClass(MyURLProtocol)
    return true

我的资源目录结构

res
└── js
    └── jquery.min.js

代码运行时出现如下错误

    0x111785900 <+0>:   pushq  %rbp
    0x111785901 <+1>:   movq   %rsp, %rbp
    0x111785904 <+4>:   pushq  %rbx
    0x111785905 <+5>:   pushq  %rax
    0x111785906 <+6>:   movq   %rdi, %rbx
    0x111785909 <+9>:   movq   0x118(%rbx), %rdi
->  0x111785910 <+16>:  movq   0x368(%rdi), %rax  [WebThread(7):EXC_BAD_ACCESS(code=1.address=0x368)].
    0x111785917 <+23>:  movq   %rax, 0x508(%rbx)

我用 lldb 多次输入 continue,但代码停留在 0x111785910

代码有什么问题?

【问题讨论】:

某事可能是零 - 你没有提供足够的信息。 your 代码的哪一行崩溃了?控制台上还显示了哪些其他消息? xx.xx.xx.xx/login.jsp?title=no xx.xx.xx.xx/login.jsp?title=no xx.xx.xx.xx/login.jsp?title=no xx.xx.xx.xx/login.jsp?title=no xx.xx.xx.xx/login.jsp?title=no xx.xx.xx.xx/js/ustc.js xx.xx.xx.xx/js/jquery.min.js (lldb) continue 进程 15696 恢复 (lldb) continue 进程 15696 恢复 (lldb) 您需要查看堆栈跟踪并查看代码中的哪一行引发了异常。或者设置断点并单步执行你的代码,看看它在哪里崩溃。 谢谢,我去试试 【参考方案1】:

我找到了解决办法,我写博客如下。 http://codeboy.me/2015/09/25/ios-uiwebview-optimization/

【讨论】:

以上是关于NSURLProtocol 详解的主要内容,如果未能解决你的问题,请参考以下文章

NSURLProtocol 使用 AJAX 超时

NSURLProtocol总结:NSURLProtocol 的本质是对特殊的scechme进行特殊的协议定制

NSURLProtocol & swift - ios7 中的错误

iOS 开发之— NSURLProtocol

在自定义 NSURLProtocol 中捕获 POST 参数

NSURLProtocol 可以与 NSURLSession upload, downloaddataTask 一起使用吗