SwiftUI中Alamofire请求API出现ResponseSerializationFailureReason错误
问题描述
- 接口调用偶发成功、偶发失败,同一请求在Postman中可正常运行
- 核心报错为响应序列化失败,提示传入数据不是合法JSON,第1行第0列位置存在无效值,使用
responseDecodable解析[Category]模型数组失败
报错信息如下:
failure(Alamofire.AFError.responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around line 1, column 0." UserInfo={NSDebugDescription=Invalid value around line 1, column 0., NSJSONSerializationErrorIndex=0}))))))
原有业务实现代码:
class TemplateObserver: ObservableObject { @Published var category = [Category]() init(){ fetchCategories() } func fetchCategories() { let request = AF.request(Constants.CATEGORIES, method: .get) request.responseDecodable(of: [Category].self) { response in print("Category Response: (response)") guard let result = response.value else { return } self.category = result } } }
原有App初始化代码:
@main struct Logo_MakerApp: App { @ObservedObject var templateObserver = TemplateObserver() var body: some Scene { WindowGroup { ContentView( templateObserver: templateObserver ) } } }
根因方向
报错指向响应起始位置(第1行第0列)就不符合JSON格式,常见触发场景:
- 接口偶发返回非JSON内容:比如网关限流、鉴权失效、服务端节点异常时返回HTML格式的错误页,或者空响应体
- 服务端返回的JSON带UTF-8 BOM头,前置3个特殊字节导致JSON解析器从起始位置就识别失败
- 生命周期声明错误导致实例重复创建、重复发请求,触发请求时序异常
- 未设置正确的Accept请求头,服务端偶发返回非JSON格式的响应
排查&修复方案
- 先打印原始响应内容
原有代码直接在解码失败时return,没有输出原始返回数据,无法定位实际拿到的内容。先将responseDecodable替换为responseData拿到原始二进制数据,转成UTF-8字符串打印,90%的场景可以直接定位问题(比如看到返回内容是<html>开头的错误页,还是空字符串)。 - 修复通用问题
- 给请求加上
Accept: application/json请求头,明确告知服务端需要返回JSON格式 - 增加UTF-8 BOM头过滤逻辑,兼容服务端不规范返回
- 所有UI和数据赋值操作显式切到主线程,避免时序问题
- 将App层级的
@ObservedObject替换为@StateObject,保证ObservableObject实例生命周期正确,不会被系统意外重建重复发起请求
- 给请求加上
修复后的可直接运行代码:
class TemplateObserver: ObservableObject { @Published var category = [Category]() init(){ fetchCategories() } func fetchCategories() { // 明确指定Accept请求头 let headers: HTTPHeaders = [ "Accept": "application/json" ] AF.request(Constants.CATEGORIES, method: .get, headers: headers) .responseData { [weak self] response in guard let self = self else { return } // 打印响应状态码,快速定位是否是HTTP错误 print("接口响应状态码: \(response.response?.statusCode ?? -1)") switch response.result { case .success(var rawData): // 过滤UTF-8 BOM头 let bomHeader: [UInt8] = [0xEF, 0xBB, 0xBF] if rawData.count >= 3, Array(rawData.prefix(3)) == bomHeader { rawData = rawData.dropFirst(3) } // 打印原始响应字符串,排查非JSON内容 if let responseText = String(data: rawData, encoding: .utf8) { print("原始响应内容: \(responseText)") } // 手动解码 do { let result = try JSONDecoder().decode([Category].self, from: rawData) DispatchQueue.main.async { self.category = result } } catch { print("JSON解码失败: \(error)") } case .failure(let reqError): print("网络请求失败: \(reqError)") } } } }
修正后的App入口代码:
@main struct Logo_MakerApp: App { // 自身持有的ObservableObject用@StateObject,保证生命周期正确 @StateObject var templateObserver = TemplateObserver() var body: some Scene { WindowGroup { ContentView(templateObserver: templateObserver) } } }
- 针对性排查
如果加了日志后发现偶发场景下返回的是403/429/502等HTTP错误码、对应内容是HTML错误页,需要和后端同学确认网关限流、鉴权有效期、负载均衡节点异常的问题,这类问题Postman因为请求频率低、带的请求头和App端有差异,往往不会触发。
内容的提问来源于stack exchange,提问作者Muhammad Farooq
相关产品推荐
相关产品推荐

