iOS POST请求JSON解析错误(NSCocoaErrorDomain Code=3840)修复咨询
分析并修复POST请求的JSON解析错误
错误原因
NSCocoaErrorDomain Code=3840 错误本质是服务器返回的内容不是标准JSON格式,结合你的代码和Postman请求场景,核心问题有两个:
- 请求格式不匹配:你用JSON格式发送带图片的请求,但服务器实际要求
multipart/form-data(文件上传的标准格式),导致服务器返回非JSON错误响应 - 数据类型错误:
rate下的availability/beauty/purity以及amount应该是数值类型,但你传递了字符串,触发服务器验证失败,返回非JSON提示
修复方案
1. 改用MultipartFormData发送请求
因为请求包含图片文件,必须使用multipart/form-data格式,而非JSON。修改后的完整代码如下:
@objc func postActionButton() { // 检查AccessToken guard let accessToken = UserDefaults.standard.string(forKey: "AccessToken") else { print("Access token is missing") return } // 验证请求URL guard let url = URL(string: "\(apiLink)/report/create_report/") else { print("Invalid URL") return } // 检查选中的图片 guard let image = selectedImage else { print("No image selected") return } guard let imageData = image.jpegData(compressionQuality: 0.8) else { print("Failed to convert image to data") return } // 构建基础请求 var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") // 配置MultipartFormData边界 let boundary = UUID().uuidString request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") var body = Data() // 添加普通文本字段 // description body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\"description\"\r\n\r\n".data(using: .utf8)!) body.append((descriptionTextView.text ?? "").data(using: .utf8)!) body.append("\r\n".data(using: .utf8)!) // report_status(固定值) body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\"report_status\"\r\n\r\n".data(using: .utf8)!) body.append("Accepted".data(using: .utf8)!) body.append("\r\n".data(using: .utf8)!) // type_obj body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\"type_obj\"\r\n\r\n".data(using: .utf8)!) body.append((selectedOption ?? "").data(using: .utf8)!) body.append("\r\n".data(using: .utf8)!) // id_obj body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\"id_obj\"\r\n\r\n".data(using: .utf8)!) body.append("\(selectedOptionID ?? 0)".data(using: .utf8)!) body.append("\r\n".data(using: .utf8)!) // 处理嵌套的rate字段(转成JSON字符串) let rateDict: [String: Any] = [ "availability": Double(rateInputView.transportTextField.text ?? "") ?? 0.0, "beauty": Double(rateInputView.beautyTextField.text ?? "") ?? 0.0, "purity": Double(rateInputView.pollutionTextField.text ?? "") ?? 0.0 ] if let rateData = try? JSONSerialization.data(withJSONObject: rateDict), let rateString = String(data: rateData, encoding: .utf8) { body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\"rate\"\r\n\r\n".data(using: .utf8)!) body.append(rateString.data(using: .utf8)!) body.append("\r\n".data(using: .utf8)!) } // 处理嵌套的Results数组(转成JSON字符串) let wasteItem: [String: Any] = [ "waste_id": [ "name": wasteInputView.wasteLabel.text ?? "", "unit_of_waste": "kg" ], "amount": Double(wasteInputView.bulbTextField.text ?? "") ?? 0.0 ] let resultsArray = [wasteItem] if let resultsData = try? JSONSerialization.data(withJSONObject: resultsArray), let resultsString = String(data: resultsData, encoding: .utf8) { body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\"Results\"\r\n\r\n".data(using: .utf8)!) body.append(resultsString.data(using: .utf8)!) body.append("\r\n".data(using: .utf8)!) } // 添加图片文件 body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\"photo\"; filename=\"image.jpg\"\r\n".data(using: .utf8)!) body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!) body.append(imageData) body.append("\r\n".data(using: .utf8)!) // 结束边界 body.append("--\(boundary)--\r\n".data(using: .utf8)!) request.httpBody = body // 发送请求并处理响应 let task = URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { print("Request error: \(error)") return } guard let data = data else { print("No data received") return } // 打印原始响应内容,方便排查非JSON错误 if let responseString = String(data: data, encoding: .utf8) { print("Raw response: \(responseString)") } do { if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] { print("Response JSON: \(json)") } } catch { print("Failed to parse JSON: \(error)") } } task.resume() }
2. 关键修改说明
- 请求格式切换:从JSON改为
multipart/form-data,符合文件上传的标准规范,匹配Postman的请求结构 - 数据类型修正:将
rate下的三个评分字段和amount从字符串转为Double类型,匹配服务器要求的数值格式 - 嵌套结构处理:把
rate和Results这类嵌套JSON结构转成字符串后,作为form-data的字段值发送 - 调试辅助:添加原始响应内容打印,方便排查服务器返回的非JSON错误提示(比如参数验证失败的文本)
3. 额外排查建议
- 检查
selectedOptionID的类型,确保和服务器要求的ID格式(整数/字符串)一致 - 验证
apiLink是否正确,避免因URL拼写错误导致服务器返回404等非JSON响应 - 如果仍有错误,查看打印的
Raw response内容,根据服务器提示调整参数
内容的提问来源于stack exchange,提问作者Alexander Chebotarev
相关产品推荐
相关产品推荐

