如何通过编程监控App中URLSession的所有网络请求?
编程监控App中URLSession的所有HTTP请求
要实现全局监控App内(包括第三方库)所有URLSession发起的HTTP/HTTPS请求,获取请求URL、头、体及响应数据,核心方案是利用自定义URLProtocol拦截请求,再通过Method Swizzling确保所有Session配置都加载该Protocol。
实现步骤
1. 自定义URLProtocol子类
URLProtocol是系统提供的请求拦截机制,通过重写其方法可以捕获、修改、转发请求,并收集请求/响应数据。
import Foundation class HTTPMonitorProtocol: URLProtocol { private var task: URLSessionTask? // 判断是否拦截当前请求 override class func canInit(with request: URLRequest) -> Bool { // 仅拦截HTTP/HTTPS请求,且避免重复拦截已处理的请求 guard let scheme = request.url?.scheme, (scheme.lowercased() == "http" || scheme.lowercased() == "https"), URLProtocol.property(forKey: "isMonitored", in: request) == nil else { return false } return true } // 返回标准化请求(通常直接返回原请求) override class func canonicalRequest(for request: URLRequest) -> URLRequest { return request } // 开始处理请求:收集请求数据,转发请求 override func startLoading() { var mutableRequest = request // 标记请求已被监控,防止重复拦截 URLProtocol.setProperty(true, forKey: "isMonitored", in: mutableRequest) // 收集请求核心信息 print("=== 请求详情 ===") print("URL: \(mutableRequest.url?.absoluteString ?? "未知")") print("方法: \(mutableRequest.httpMethod ?? "未知")") print("请求头: \(mutableRequest.allHTTPHeaderFields ?? [:])") if let bodyData = mutableRequest.httpBody, let bodyStr = String(data: bodyData, encoding: .utf8) { print("请求体: \(bodyStr)") } // 创建Session转发请求,自身作为代理处理响应 let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil) task = session.dataTask(with: mutableRequest) task?.resume() } // 停止加载时清理资源 override func stopLoading() { task?.cancel() task = nil } } // 扩展实现URLSession代理,收集响应数据 extension HTTPMonitorProtocol: URLSessionDataDelegate { func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { // 收集响应信息 print("=== 响应详情 ===") if let httpResponse = response as? HTTPURLResponse { print("状态码: \(httpResponse.statusCode)") print("响应头: \(httpResponse.allHeaderFields)") } // 将响应传递给原请求的客户端,保证业务逻辑正常执行 client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) completionHandler(.allow) } func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { // 收集返回数据 if let dataStr = String(data: data, encoding: .utf8) { print("返回数据: \(dataStr)") } // 将数据传递给原请求的客户端 client?.urlProtocol(self, didLoad: data) } func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { // 收集错误信息 if let error = error { print("请求失败: \(error.localizedDescription)") client?.urlProtocol(self, didFailWithError: error) } else { client?.urlProtocolDidFinishLoading(self) } // 销毁Session,避免内存泄漏 session.finishTasksAndInvalidate() } }
2. 全局注册并Hook Session配置
仅注册URLProtocol无法覆盖第三方库自定义的URLSessionConfiguration,需通过Method Swizzling修改所有配置的protocolClasses,确保自定义Protocol被加入。
import Foundation extension URLSessionConfiguration { static func setupHTTPMonitoring() { // 注册自定义Protocol URLProtocol.registerClass(HTTPMonitorProtocol.self) // Swizzle protocolClasses getter,自动添加自定义Protocol swizzleProtocolClasses() } private static func swizzleProtocolClasses() { let originalSelector = #selector(getter: protocolClasses) let swizzledSelector = #selector(getter: swizzled_protocolClasses) guard let originalMethod = class_getInstanceMethod(self, originalSelector), let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else { return } method_exchangeImplementations(originalMethod, swizzledMethod) } @objc private var swizzled_protocolClasses: [AnyClass]? { // 调用原方法获取默认的Protocol列表 let originalClasses = swizzled_protocolClasses var modifiedClasses = originalClasses ?? [] // 避免重复添加自定义Protocol if !modifiedClasses.contains(HTTPMonitorProtocol.self) { modifiedClasses.insert(HTTPMonitorProtocol.self, at: 0) } return modifiedClasses } }
3. 在App启动时初始化监控
在AppDelegate或SceneDelegate的启动方法中调用初始化代码:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 开启HTTP请求监控(可通过开关控制生产环境是否启用) #if DEBUG URLSessionConfiguration.setupHTTPMonitoring() #endif // 或者用远程配置控制: // if RemoteConfig.shared.isHTTPMonitoringEnabled { // URLSessionConfiguration.setupHTTPMonitoring() // } return true }
生产环境注意事项
- 开关控制:务必通过编译宏、远程配置等方式控制监控的开启/关闭,避免影响线上性能。
- 性能优化:请求/响应数据的收集、存储或上报需放在异步线程,避免阻塞主线程;避免在监控逻辑中做耗时操作。
- 特殊场景处理:若请求体使用
httpBodyStream,需额外读取流数据;若App有证书校验逻辑,需在自定义Protocol中同步实现。 - 内存泄漏:确保
stopLoading方法中清理Task和Session资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Silver Ringvee
相关产品推荐
相关产品推荐

