You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 01:36:00