Swift中Multipart Form-Data请求体为空问题排查
Swift Multipart Form-Data 请求体为空问题排查与修复
问题定位
你的代码存在两个核心错误,直接导致请求体异常:
- 文本字段编码错误:在添加
data字段时,直接将字符串追加到Data类型的httpBody中,默认编码不匹配Multipart格式要求,会引发数据丢失或格式混乱。 - 边界字符串拼写错误:结构体中
boundarry是拼写错误(正确应为boundary),虽当前未直接引发问题,但属于潜在bug,需统一修正。
修复后的完整代码
1. 重构Multipart表单构造结构体
public struct RequestMultipartFormData { private let boundary: String = UUID().uuidString.replacingOccurrences(of: "-", with: "") private var httpBody = Data() let url: URL public init(url: URL) { self.url = url } // 构造文件字段 private func dataFormField(fieldName: String, imageName: String, fileData: Data, mimeType: String) -> Data { let fieldData = NSMutableData() fieldData.append("--\(boundary)\r\n".data(using: .utf8)!) fieldData.append("Content-Disposition: form-data; name=\"\(fieldName)\"; filename=\"\(imageName)\"\r\n".data(using: .utf8)!) fieldData.append("Content-Type: \(mimeType)\r\n\r\n".data(using: .utf8)!) fieldData.append(fileData) fieldData.append("\r\n".data(using: .utf8)!) print("Added file field - \(fieldName): \(fileData.count) bytes") return fieldData as Data } public mutating func addFileField(fieldName: String, imageName: String, fileData: Data, mimeType: String) { let fieldData = dataFormField(fieldName: fieldName, imageName: imageName, fileData: fileData, mimeType: mimeType) httpBody.append(fieldData) } // 构造文本字段(修正编码问题) private func addTextField(named name: String, value: String) -> Data { var dataString = "--\(boundary)\r\n" dataString += "Content-Disposition: form-data; name=\"\(name)\"\r\n" dataString += "Content-Type: application/json; charset=utf-8\r\n" dataString += "\r\n" dataString += "\(value)\r\n" print("Boundary: --\(boundary)") print("Adding Text Field - \(name): \(value)") return dataString.data(using: .utf8)! } public mutating func addTextField(named name: String, value: String ) { let fieldData = addTextField(named: name, value: value) httpBody.append(fieldData) } // 生成最终请求 public mutating func createURLRequest() -> URLRequest? { var request = URLRequest(url: url) guard let sessionValue = AppConfig.AppStore["sessionId"] as? String else { print("Failed to retrieve session ID") return nil } request.setValue(sessionValue, forHTTPHeaderField: "session_id") if let token = AppConfig.AppStore["token"] as? String { request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } request.addValue("APP", forHTTPHeaderField: "User-Agent") request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" // 添加结束边界 httpBody.append("--\(boundary)--\r\n".data(using: .utf8)!) request.httpBody = httpBody print("Request Headers: \(request.allHTTPHeaderFields ?? [:])") print("Request Body: \(String(data: request.httpBody ?? Data(), encoding: .utf8) ?? \"No Body\")") return request } }
2. 修正接口调用方法
public func chamaMultipartTwo(requestBody model: ChamaOfficialsPhotoUploadRequestTwo, selectedImage: UIImage?, imageName: String, _ completion: @escaping (Bool, String?) -> ()) { let encoder = JSONEncoder() guard let jsonData = try? encoder.encode(model), let jsonString = String(data: jsonData, encoding: .utf8) else { completion(false, "Failed to encode the struct to jsonString") return } print("Request Payload: \(jsonString)") let baseUrl = "https://mbankingtest.postbank.co.ke:9193/" let endpointPath = "mchama/api/gateway/files" guard let url = URL(string: baseUrl + endpointPath) else { completion(false, "Invalid URL") return } var requestForm = RequestMultipartFormData(url: url) if let selectedImage = selectedImage { guard let imageData = selectedImage.jpegData(compressionQuality: 0.7) else { completion(false, "Failed to convert UIImage to JPEG data") return } print("my image data \(imageData.prefix(100))") requestForm.addFileField( fieldName: "files", imageName: imageName, fileData: imageData, mimeType: "image/jpeg") } requestForm.addTextField(named: "data", value: jsonString) guard let request = requestForm.createURLRequest() else { completion(false, "Failed to create request") return } let session = URLSession.shared session.dataTask(with: request) {(data, response, error) in if let error = error { completion(false, error.localizedDescription) return } if let response = response { print("Response: \(response)") } if let data = data { do { let json = try JSONSerialization.jsonObject(with: data, options: []) print("JSON response: \(json)") completion(true, "Success") } catch { print("Error decoding JSON response: \(error)") completion(false, error.localizedDescription) } } }.resume() }
额外优化说明
- 采用Swift规范命名:结构体和方法名使用大驼峰/小驼峰格式,提升代码可读性。
- 统一编码格式:文本字段
Content-Type指定为utf-8,避免编码兼容性问题。 - 移除冗余
mutating:无需修改结构体属性的方法移除mutating关键字,符合Swift语法规范。
内容的提问来源于stack exchange,提问作者Collins Muthomi
相关产品推荐
相关产品推荐

