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

Swift中Multipart Form-Data请求体为空问题排查

Swift Multipart Form-Data 请求体为空问题排查与修复

问题定位

你的代码存在两个核心错误,直接导致请求体异常:

  1. 文本字段编码错误:在添加data字段时,直接将字符串追加到Data类型的httpBody中,默认编码不匹配Multipart格式要求,会引发数据丢失或格式混乱。
  2. 边界字符串拼写错误:结构体中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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 08:29:55