Search code examples
swiftresthttp-parameters

400 error when making post request to local server in Swift


I am trying to make an http POST request to a local server, but when I make the call, I get a 400 error on the server side, so I believe there is a problem in the way I am making the request in the Swift code. I am not sure if I am setting the JSON body correctly, but any help would be greatly appreciated. Thanks in advance!

swift code:

    let url = URL(string: "http://127.0.0.1:5000/get-friends")!
    var request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10)
    request.httpMethod = "POST"
    let tempIdDictionary: [String: Int] = ["user_id": 17]
    let jsonBody = try? JSONSerialization.data(withJSONObject: tempIdDictionary, options: .fragmentsAllowed)
    request.httpBody = jsonBody
    
    let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
        guard let data = data else { return }
        do {
            let friendList = try JSONDecoder().decode(FriendList.self, from: data)
        } catch let jsonErr {
            print(jsonErr)
        }
    }
    task.resume()

struct to hold the json data:

struct FriendList: Codable {
    let result: [Int]
}

this is the error that shows up in swift when I try to make the request:

dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))

Postman request


Solution

  • Update: From the screenshot that you've added just now it's evident that you need to pass the data as form-data instead of json. Here's how you do this, add the following extensions:

    extension URLRequest {
    
        struct MultipartFile {
            var data: Data
            var mimeType, filename: String
        }
    
        mutating func multipartFormData(
            parameters: [String: String] = [:],
            files: [MultipartFile] = []) {
    
            var body = Data()
            let boundary = "Boundary-\(UUID().uuidString)"
            let boundaryPrefix = "--\(boundary)\r\n"
    
            for (key, value) in parameters {
                body + boundaryPrefix
                body + "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n"
                body + "\(value)\r\n"
            }
    
            for file in files {
                body + boundaryPrefix
                body + "Content-Disposition: form-data; name=\"file\"; filename=\"\(file.filename)\"\r\n"
                body + "Content-Type: \(file.mimeType)\r\n\r\n"
                body.append(file.data)
                body + "\r\n"
            }
    
            body + "--".appending(boundary.appending("--"))
    
            httpBody = body
            httpMethod = httpMethod == "GET" ? "POST" : httpMethod
            setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
        }
    }
    
    extension Data {
    
        static func + (data: inout Data, string: String) {
            data.append(Data(string.utf8))
        }
    }
    

    Then modify the request as follows:

    request.httpMethod = "POST"
    request.multipartFormData(parameters: ["user_id": 17])
    // remove the `JSON` body part
    let task = URLSession.shared.dataTask(with: request) { (data, response, error) in