Alamofire 5上传解码失败:提示数据缺失却实际存在的问题
问题现象
使用Alamofire 5上传数据时,控制台提示「数据无法读取,因为数据缺失」,但上传的数据实际存在。控制台输出如下:
dic=== {"lat": "26.9342246", "message": "Test", "lng": "-80.0942087", "receiver_id": "269", "messageType": "text", "chatType": "fix", "product_variant_id": "", "receiverType": "customer", "customer_id": "213", "itemType": "sell", "product_id": "25", "service_id": "", "fileName": ""}
Result: failure(Alamofire.AFError.responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "message", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "message", intValue: nil) ("message").", underlyingError: nil)))))
Upload Failed: Response could not be decoded because of error:
The data couldn’t be read because it is missing.
尝试将MessageModel中的message改为可选类型后,又出现「意外发现nil」错误,错误触发代码如下:
func sendChatProductImage(header: String, parameter: String, coverdata: UIImage, mimetype : String ,vc: UIViewController, showHud: Bool,completion : @escaping(_ success : Bool, _ message : String, _ response : NSDictionary) -> Void) { webService_obj.chatProductImageSend(header: header, withParameter: parameter, coverdata: coverdata, mimetypeExtention: mimetype, inVC: vc, showHud: showHud) { (response, success) in if success{ let message = response["message"] completion(success, message as! String, response ) } } }
相关代码定义
Decodable 结构体
struct MessageModel : Decodable { let data : [MessageModelData]? let message : String enum CodingKeys: String, CodingKey { case message, data } } struct MessageModelData : Decodable { let fileType : String let groupID, id : Int let intetestCustomerID, itemType, message, messageType : String let productID, productOwnerID, profileImg, profileName :String let receiveID, receiverType, sendAt : String enum CodingKeys: String, CodingKey { case groupID = "groupId" case intetestCustomerID = "intetest_customer_id" case productID = "product_id" case productOwnerID = "product_owner_id" case receiveID = "receiveId" case fileType, id, itemType, message, messageType, profileImg, profileName, receiverType, sendAt } }
Alamofire 上传代码
AF.upload( multipartFormData: { multipartFormData in var dic = parameter.replacingOccurrences(of: "[", with: "{") dic = dic.replacingOccurrences(of: "]", with: "}") print("dic===",dic) multipartFormData.append("\(dic)".data(using: String.Encoding.utf8)!, withName: "data") if coverimageData != nil { multipartFormData.append(coverimageData!, withName: "file", fileName: "chatImage.jpg", mimeType: "*/*") }else{ multipartFormData.append("".data(using: String.Encoding.utf8)!, withName: "file") } }, with: uploadUrl).responseDecodable(of: MessageModel.self) { response in print("Result: \(response)") if hud { Loader.init().hide(delegate: vc) } switch response.result { case .success(let result): let dataResponse = response.value(forKey: "data" as? NSDictionary) completion(dataResponse, true) case .failure(let error): let err = error.localizedDescription print("Upload Failed: \(err)") completion([:], false) self.presentAlertWithTitle(title: kAPPName, message: err, vc: vc) break } }
问题原因及修复方案
核心问题
- 解码结构不匹配:
responseDecodable要求服务器返回的JSON结构与MessageModel完全一致,但实际返回的响应可能不符合该结构(比如message字段不存在、嵌套层级错误);同时上传代码中用字符串替换符号构造JSON的方式,容易产生格式错误,导致服务器无法正确解析请求,进而返回异常响应。 - 强制解包触发崩溃:将
message改为可选类型后,代码中仍用message as! String强制解包,若服务器返回的message为nil或类型不匹配,就会触发nil崩溃。
修复步骤
1. 验证服务器真实响应结构
在解码失败的分支中打印原始响应数据,确认服务器返回的JSON格式:
case .failure(let error): if let data = response.data, let rawResponse = String(data: data, encoding: .utf8) { print("服务器原始响应:\(rawResponse)") } // 原有报错逻辑
根据实际返回结构调整MessageModel,比如将可能缺失的字段设为可选:
struct MessageModel : Decodable { let data : [MessageModelData]? let message : String? // 改为可选类型 }
2. 修复请求参数构造方式
放弃字符串替换的方式,直接将参数序列化为合法的JSON Data:
// 若parameter是字典类型 if let paramDict = try? JSONSerialization.jsonObject(with: parameter.data(using: .utf8)!) as? [String: Any], let paramData = try? JSONSerialization.data(withJSONObject: paramDict, options: []) { multipartFormData.append(paramData, withName: "data") } // 若parameter本身是合法JSON字符串,直接转Data if let paramData = parameter.data(using: .utf8) { multipartFormData.append(paramData, withName: "data") }
3. 替换强制解包为安全处理
在sendChatProductImage中对可选值做安全处理:
if success{ let message = response["message"] as? String ?? "操作完成" completion(success, message, response ) }
4. 修正解码后的数据处理逻辑
直接使用解码后的result对象,避免KVC方式取值:
case .success(let result): let dataResponse = result.data as? NSDictionary ?? [:] completion(dataResponse, true)
内容的提问来源于stack exchange,提问作者Zach Handley

