Swift开发:URLSession请求间歇性失效排查求助
问题排查与解决方案
1. 核心原因:缺失User-Agent请求头
多数公共API会通过验证User-Agent字段识别合法请求,未携带该字段的请求可能被服务器的反爬虫策略间歇性拦截。Postman会自动添加标准的User-Agent,所以请求稳定;而URLRequest默认不携带该字段,导致你的请求被服务器偶尔拒绝。
2. 代码中的其他隐患
- 未处理
data和error同时为空的场景:这种情况你的代码直接返回,上层调用者无法感知请求失败,误以为"无响应"。 - 未校验HTTP响应状态码:即使拿到数据,也可能是服务器返回的错误响应(如429限流、500服务错误),直接解码会导致无意义的失败。
- 默认超时时间偏短:API返回数据体积较大(近600KB),网络波动时容易触发超时。
3. 修改后的代码
import Foundation final class APIService { // 模拟浏览器请求的User-Agent,避免被拦截 private let defaultUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" init(){} struct Constants{ static let apiURL = URL(string: "https://public.opendatasoft.com/api/explore/v2.1/catalog/datasets/airbnb-listings/records?limit=100&refine=city%3A%22New%20York%22&refine=room_type%3A%22Entire%20home%2Fapt%22") } public func getListings(completion: @escaping (Result<[WindbnbListing], Error>) -> Void) { guard let url = Constants.apiURL else { completion(.failure(NSError(domain: "APIService", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效的API地址"]))) return } var request = URLRequest(url: url) // 添加User-Agent请求头 request.setValue(defaultUserAgent, forHTTPHeaderField: "User-Agent") // 延长超时时间,适配大体积数据返回 request.timeoutInterval = 30 let task = URLSession.shared.dataTask(with: request) { data, response, error in // 处理请求错误 if let error { completion(.failure(error)) return } // 处理无响应数据的情况 guard let data = data else { completion(.failure(NSError(domain: "APIService", code: -2, userInfo: [NSLocalizedDescriptionKey: "未获取到响应数据"]))) return } // 校验HTTP状态码是否合法 guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { let statusCode = (response as? HTTPURLResponse)?.statusCode ?? -1 let error = NSError(domain: "APIService", code: statusCode, userInfo: [NSLocalizedDescriptionKey: "请求失败,状态码:\(statusCode)"]) // 打印错误响应内容方便排查 if let json = try? JSONSerialization.jsonObject(with: data) { print("错误响应:\(json)") } completion(.failure(error)) return } do{ let response = try JSONDecoder().decode(WindbnbListingsResponse.self, from: data) completion(.success(response.results)) }catch{ let json = try? JSONSerialization.jsonObject(with: data) print("解码失败,响应内容:\(String(describing: json))") completion(.failure(error)) } } task.resume() } }
4. 额外建议
- 给
WindbnbListing的非可选字段做容错:比如host_name等字段,如果API返回为空会导致解码失败,建议改为可选类型或设置默认值。 - 增加请求重试机制:遇到429限流、超时等可恢复错误时,自动重试1-2次(注意控制重试间隔,避免触发更严格的限流)。
内容的提问来源于stack exchange,提问作者OzosStackOverflow
相关产品推荐
相关产品推荐

