SwiftUI中如何通过指定浏览器VPN连接发起URL请求获取内网JSON
自定义协议触发VPN并请求内网数据的问题解决
问题场景
在iPad应用中需从外网访问内网获取JSON数据,已配置的VPN仅在VMWare浏览器中生效,Safari使用普通网络连接。尝试用awbs://替代https://触发VPN配置,无需打开浏览器直接发起请求,但修改代码后出现unsupported URL错误。
相关代码
func getData() async { @State var value: [responsejson]? guard let url = URL(string: "https:/...") else { print("Invalid URL") return } let request = URLRequest(url: url) URLSession.shared.dataTask(with: request) {data, response, error in if let data = data { if let decodedResponse = try? JSONDecoder().decode(responsejson.self, from: data) { DispatchQueue.main.async { self.response = decodedResponse.text } return } } print("Error: \(error?.localizedDescription ?? "Unknown error")") }.resume() }
报错信息
Task <1039D056-FDC4-46EE-9635-373AF8723409>.<1> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL, NSErrorFailingURLStringKey=awbs://(...)
问题原因
URLSession仅支持系统默认的网络协议(如http://、https://),自定义协议awbs://不在其支持范围内,因此直接替换协议会触发unsupported URL错误。另外,自定义协议的设计初衷是唤起第三方应用(如mailto://唤起邮件),而非直接发起网络请求。
解决方案
方案1:让应用直接使用VPN网络通道
检查VPN的应用权限配置,将你的iPad应用添加到VPN的允许列表中。部分VPN支持按应用隔离网络,添加后应用发起的https://请求会自动走VPN通道,无需修改协议。
方案2:自定义URLProtocol处理awbs协议
通过自定义URLProtocol拦截awbs://请求,将其转换为https://的内网请求,同时确保请求走VPN通道。示例代码如下:
class AWBSURLProtocol: URLProtocol { override class func canInit(with request: URLRequest) -> Bool { return request.url?.scheme == "awbs" } override class func canonicalRequest(for request: URLRequest) -> URLRequest { return request } override func startLoading() { guard let url = request.url else { client?.urlProtocol(self, didFailWithError: NSError(domain: "AWBSProtocol", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效URL"])) return } // 将awbs://转换为https:// guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false), var httpsComponents = components.copy() as? URLComponents else { client?.urlProtocol(self, didFailWithError: NSError(domain: "AWBSProtocol", code: -2, userInfo: [NSLocalizedDescriptionKey: "转换URL失败"])) return } httpsComponents.scheme = "https" guard let httpsURL = httpsComponents.url else { client?.urlProtocol(self, didFailWithError: NSError(domain: "AWBSProtocol", code: -3, userInfo: [NSLocalizedDescriptionKey: "生成HTTPS URL失败"])) return } var modifiedRequest = request modifiedRequest.url = httpsURL // 发起转换后的请求 let task = URLSession.shared.dataTask(with: modifiedRequest) { [weak self] data, response, error in guard let self = self else { return } if let error = error { self.client?.urlProtocol(self, didFailWithError: error) return } if let response = response { self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) } if let data = data { self.client?.urlProtocol(self, didLoad: data) } self.client?.urlProtocolDidFinishLoading(self) } task.resume() } override func stopLoading() { // 可添加任务取消逻辑 } } // 在应用启动时注册自定义Protocol func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { URLProtocol.registerClass(AWBSURLProtocol.self) return true }
注意:使用前需确保VPN已激活,或在转换请求前先启动VPN。
方案3:先唤起VPN再发起HTTPS请求
如果VPN提供了URL Scheme唤起接口(如vpnprovider://connect),可先调用该Scheme启动VPN,待连接成功后再发起原https://请求:
func startVPNThenFetchData() { guard let vpnURL = URL(string: "vpnprovider://connect") else { print("无效的VPN唤起URL") return } UIApplication.shared.open(vpnURL) { success in if success { // 等待VPN连接(等待时间根据实际情况调整) DispatchQueue.main.asyncAfter(deadline: .now() + 5) { self.getData() } } else { print("唤起VPN失败") } } }
内容的提问来源于stack exchange,提问作者larud
相关产品推荐
相关产品推荐

