Swift带证书的REST请求返回-999错误,求问题排查
错误代码 -999 对应的是 NSURLErrorCancelled,意味着你的登录请求在完成前被主动取消了。结合你的代码和场景,主要有以下几个核心问题点,我们逐一排查修复:
1. SessionManager配置错误:未使用自定义配置实例
看你初始化 almgr 的代码,你明明创建并配置了自定义的 URLSessionConfiguration,但最后创建SessionManager时却误用了全新的 URLSessionConfiguration.default,这会导致你设置的HTTP头和自定义配置完全失效,进而引发请求异常:
// 错误代码片段 let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders // 这里错误地使用了新的默认配置,而非上面已经配置好的configuration let man = Alamofire.SessionManager(configuration: URLSessionConfiguration.default, serverTrustPolicyManager: serverTrustPolicyManager)
修复方案:
替换为你已经配置好的 configuration 实例:
let man = Alamofire.SessionManager(configuration: configuration, serverTrustPolicyManager: serverTrustPolicyManager)
2. 服务器信任策略的Host不匹配
你设置的服务器信任策略的host是 "liper":
let serverTrustPolicies = ["liper":trustPolicy]
但如果你的 REST_MAIN_URL 的实际域名是类似 "liper.yourdomain.com" 或者其他变体,这个不匹配会导致Alamofire拒绝信任服务器,直接取消请求。
修复方案:
确保 serverTrustPolicies 中的key和你请求URL的host完全一致。比如如果你的接口地址是 https://api.liper.com/login,那么key应该是 "api.liper.com"。
3. 证书文件未正确添加到项目中
你加载证书的代码用了强制解包 !,如果证书文件 certf.cer 没有被正确添加到项目Target中,或者文件名存在大小写拼写错误(iOS的Bundle文件名区分大小写),会导致证书加载失败,进而引发信任验证失败,请求被取消。
检查与修复:
- 确认
certf.cer文件在Xcode项目中,并且在「Target Membership」中勾选了你的主App Target。 - 改用安全的非强制解包写法,方便排查问题:
class func getCertificates() -> [SecCertificate]{ guard let url = Bundle.main.url(forResource: "certf", withExtension: "cer") else { print("Certificate file not found in bundle") return [] } do { let localCertificate = try Data(contentsOf: url) as CFData guard let certificate = SecCertificateCreateWithData(nil, localCertificate) else { print("Failed to create certificate from data") return[] } return [certificate] } catch { print("Failed to load certificate data: \(error)") return [] } }
4. 避免强制解包导致的潜在崩溃
在登录请求的回调中,你使用了 try! 来解析JSON:
guard let receivedResponse = try! JSONSerialization.jsonObject(with: response.data!, options: []) as? [String:Any] else { ... }
如果 response.data 为nil,或者JSON解析失败,这行代码会直接崩溃,同时可能导致请求流程异常中断。
修复方案:
改用 do-catch 安全解析:
case .success: print("\(self.TAG), receiving response from login with \(response)") guard let data = response.data else { print("\(self.TAG), No data received from login response") return } do { guard let receivedResponse = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] else { print("\(self.TAG), Error parsing response from login for json") return } // 后续处理token逻辑 if let token:String = receivedResponse["token"] as? String { print("\(self.TAG), \(token)") } else { print("\(self.TAG), error receiving token") if let errorMessage:String = receivedResponse["status"] as? String { print("\(self.TAG), error message for login with received response status: \(errorMessage)") } return } } catch { print("\(self.TAG), JSON parsing error: \(error)") return }
5. 确认单例调用的正确性
你的 RestService 是单例模式,初始化是 private init(),这部分没问题,但要确保你在控制器中调用的是 RestService.shared.loginRest(...),避免意外创建多个实例导致的请求异常。
按照以上步骤修复后,应该能解决请求被取消的问题。
内容的提问来源于stack exchange,提问作者M. Wojcik

