jmac
jmac

Reputation: 574

POST request using application/x-www-form-urlencoded

The back end developer had given these instructions in POST requests:

  1. Route: {url}/{app_name/{controller}/{action}
  2. The controller and action should be on small caps.
  3. API test link: http:****************
  4. Request should be use POST Method.
  5. Parameters should be pass via request content body (FormUrlEncodedContent).
  6. Parameters should be on json format.
  7. Parameters are key sensitive.

Having no experience with number 5 in the protocol, I searched and ended with my code.

-(id)initWithURLString:(NSString *)URLString withHTTPMEthod:(NSString *)method withHTTPBody:(NSDictionary *)body {

    _URLString = URLString;
    HTTPMethod = method;
    HTTPBody = body;

    //set error message
    errorMessage = @"Can't connect to server at this moment. Try again later";
    errorTitle = @"Connection Error";

    return  self;
}


-(void)fireConnectionRequest {

    NSOperationQueue *mainQueue = [[NSOperationQueue alloc] init];
    [mainQueue setMaxConcurrentOperationCount:5];

    NSError *error = Nil;

    NSURL *url = [NSURL URLWithString:_URLString];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];

    NSData *sendData = [NSJSONSerialization dataWithJSONObject:HTTPBody options:NSJSONWritingPrettyPrinted error:&error];
    [request setHTTPMethod:@"POST"];

    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];

    [request setHTTPBody: sendData];
    [NSURLConnection connectionWithRequest:request delegate:self];

    NSString *jsonString = [[NSString alloc]initWithData:sendData encoding:NSUTF8StringEncoding];


    //fire URL connectiion request
    [NSURLConnection sendAsynchronousRequest:request queue:mainQueue completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *error) {

        //get the return message and transform to dictionary
        NSString *data = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
        returnMessage = [NSJSONSerialization JSONObjectWithData: [data dataUsingEncoding:NSUTF8StringEncoding]
                                                        options: NSJSONReadingMutableContainers
                                                          error:&error];


        //check return message
        if (!error) {
            [delegate returnMessageForTag:self.tag];

        }
        else {
            [delegate returnErrorMessageForTag:self.tag];
        }

    }];

}

I pass a dictionary formatted to JSON. he agrees that I was able to pass the right data. And I was able to connect to the API, but it is always returning "FAILED" when I try send data for registration. There are no problems in connection, but I failed to transfer the data.

The android developer here using the same API has no problem with it, but wasn't able to help me out since he's not familiar with iOS.

What am I missing?

Upvotes: 40

Views: 78338

Answers (11)

freedomHongKong
freedomHongKong

Reputation: 101

As of Swift 5, the following code has been tested to work today.

let url = URL(string: "https://something")
var request = URLRequest(url: url!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let post = "parameter1=abc&parameter2=abc"
let postData = post.data(using: String.Encoding.ascii, allowLossyConversion: true)!
request.httpBody = postData

Upvotes: 1

inam
inam

Reputation: 533

Swift 4

let params = ["password":873311,"username":"jadon","client_id":"a793fb82-c978-11e9-a32f-2a2ae2dbcce4"]
let jsonString = params.reduce("") { "\($0)\($1.0)=\($1.1)&" }.dropLast()
let jsonData = jsonString.data(using: .utf8, allowLossyConversion: false)!
urlRequest.addValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type")
urlRequest.httpBody  = jsonData 

Upvotes: 13

Zmey
Zmey

Reputation: 2559

This version handles encoding of parameters and replacing spaces with '+'.

extension String {
    static let formUrlencodedAllowedCharacters =
        CharacterSet(charactersIn: "0123456789" +
            "abcdefghijklmnopqrstuvwxyz" +
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
            "-._* ")

    public func formUrlencoded() -> String {
        let encoded = addingPercentEncoding(withAllowedCharacters: String.formUrlencodedAllowedCharacters)
        return encoded?.replacingOccurrences(of: " ", with: "+") ?? ""
    }
}

class HTTPUtils {
    public class func formUrlencode(_ values: [String: String]) -> String {
        return values.map { key, value in
            return "\(key.formUrlencoded())=\(value.formUrlencoded())"
        }.joined(separator: "&")
    }
}

let headers = [
    "content-type": "application/x-www-form-urlencoded; charset=utf-8"
]

let body = HTTPUtils.formUrlencode([
    "field": "value"
])

var request = try URLRequest(url: url, method: .post, headers: headers)
request.httpBody = body.data(using: .utf8)

URLSession.shared.dataTask(with: request, completionHandler: { ... }).resume()

Upvotes: 5

Mantas Laurinavičius
Mantas Laurinavičius

Reputation: 951

Parse dictionary params to string:

-(NSData *)encodeParameters:(NSDictionary *)parameters {

NSMutableArray *list = [NSMutableArray new];

for (NSString *key in [parameters allKeys]) {
    id obj = [parameters objectForKey:key];
    NSString *path = [NSString stringWithFormat:@"%@=%@", key, obj];
    [list addObject:path];
}

return [[list componentsJoinedByString:@"&"] dataUsingEncoding:NSUTF8StringEncoding];

}

And use it:

[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[self encodeParameters:parameters]];

Upvotes: 0

Darshan Kunjadiya
Darshan Kunjadiya

Reputation: 3329

Try like this code

Objective C

NSString *post =[NSString stringWithFormat:@"AgencyId=1&UserId=1&Type=1&Date=%@&Time=%@&Coords=%@&Image=h32979`7~U@)01123737373773&SeverityLevel=2",strDateLocal,strDateTime,dict];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%d",[postData length]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://google/places"]]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];
NSError *error;
NSURLResponse *response;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *str=[[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];

Swift 2.2

var post = "AgencyId=1&UserId=1&Type=1&Date=\(strDateLocal)&Time=\(strDateTime)&Coords=\(dict)&Image=h32979`7~U@)01123737373773&SeverityLevel=2"
var postData = post.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)!
var postLength = "\(postData.length)"
var request = NSMutableURLRequest()
request.URL = NSURL(string: "http://google/places")!
request.HTTPMethod = "POST"
request.setValue(postLength, forHTTPHeaderField: "Content-Length")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.HTTPBody = postData
NSError * error
NSURLResponse * response
var urlData = try! NSURLConnection.sendSynchronousRequest(request, returningResponse: response)!
var str = String(data: urlData, encoding: NSUTF8StringEncoding)

Swift 3.0

let jsonData = try? JSONSerialization.data(withJSONObject: kParameters)
    let url: URL = URL(string: "Add Your API URL HERE")!
    print(url)
    var request: URLRequest = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = jsonData
    request.setValue(Constant.UserDefaults.object(forKey: "Authorization") as! String?, forHTTPHeaderField: "Authorization")
    request.setValue(Constant.kAppContentType, forHTTPHeaderField: "Content-Type")
    request.setValue(Constant.UserAgentFormat(), forHTTPHeaderField: "User-Agent")

    let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in

        if data != nil {

            do {
                let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! NSDictionary
                print(json)
            } catch let error as NSError {
                print(error)
            }
        } else {
            let emptyDict = NSDictionary()
        }
    })
    task.resume()

Swift 4

let headers = [
            "Content-Type": "application/x-www-form-urlencoded"
        ]

    let postData = NSMutableData(data: "UserID=351".data(using: String.Encoding.utf8)!)
    let request = NSMutableURLRequest(url: NSURL(string: "Add Your URL Here")! as URL,
                                      cachePolicy: .useProtocolCachePolicy,
                                      timeoutInterval: 10.0)
    request.httpMethod = "POST"
    request.allHTTPHeaderFields = headers
    request.httpBody = postData as Data

    let session = URLSession.shared
    let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
        if (error != nil) {
            print(error!)
        } else {
            let httpResponse = response as? HTTPURLResponse
            print(httpResponse!)

            do {
                let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments)
                print(json)
            } catch {
                print(error)
            }

        }
    })

    dataTask.resume()

Alamofire

Alamofire.request("Add Your URL Here",method: .post, parameters: ["CategoryId": "15"])
        .validate(contentType: ["application/x-www-form-urlencoded"])
        .responseJSON { (response) in

            print(response.result.value)

    }

I hope this code useful for you.

Upvotes: 49

Collierton
Collierton

Reputation: 549

Swift does offer a function for URL-%-encoding, but it is not an exact match as noted by @nolanw in the 1st comment. For Step 5 in the original question, once you have the key-value pairs in some structure, here is a short and simple alternative for encoding (Swift 4.2):

var urlParser = URLComponents()
urlParser.queryItems = [
    URLQueryItem(name: "name", value: "Tim Tebow"),
    URLQueryItem(name: "desc", value: "Gators' QB")
]
let httpBodyString = urlParser.percentEncodedQuery

Paste that into an Xcode playground, and add print(httpBodyString!). In the output, you will see:

name=Tim%20Tebow&desc=Gators'%20QB

Note: This is for percent-encoding set of basic form values (i.e. not binary data and not multi-part)

Upvotes: 7

Gurjinder Singh
Gurjinder Singh

Reputation: 10299

Swift 4.2

    func percentEscapeString(_ string: String) -> String {
            var characterSet = CharacterSet.alphanumerics
            characterSet.insert(charactersIn: "-._* ")
            return string
              .addingPercentEncoding(withAllowedCharacters: characterSet)!
              .replacingOccurrences(of: " ", with: " ")
              .replacingOccurrences(of: " ", with: " ", options: [], range: nil)
              .replacingOccurrences(of: "\"", with: "", options: NSString.CompareOptions.literal, range:nil)
          }
//    Set encoded values to Dict values you can decode keys if required
    dictData.forEach { (key, value) in
              if let val = value as? String {
                dictData[key] = self.percentEscapeString(val)
              } else {
                dictData[key] = value
              }
            }

This worked for me and here is link of source https://gist.github.com/HomerJSimpson/80c95f0424b8e9718a40

Upvotes: 2

fatihyildizhan
fatihyildizhan

Reputation: 8832

Is it possible to convert this code to swift ? I already tried but could not handle it. Maybe this code block may help you. Thanks.

    let myParams:NSString = "username=user1&password=12345"
    let myParamsNSData:NSData = NSData(base64EncodedString: myParams, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)!
    let myParamsLength:NSString = NSString(UTF8String: myParamsNSData.length)
    let myRequest: NSMutableURLRequest = NSURL(fileURLWithPath: self.url)
    myRequest.HTTPMethod = "POST"
    myRequest.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    myRequest.HTTPBody = myParamsNSData
    var data2: NSData!
    var error2: NSError!

Upvotes: 0

Arturo Silva
Arturo Silva

Reputation: 11

let params:[String: Any]
if "application/x-www-form-urlencoded" {
let bodyData = params.stringFromHttpParameters()
self.request.httpBody = bodyData.data(using: String.Encoding.utf8)}
if "application/json"{
  do {
    self.request.httpBody = try JSONSerialization.data(withJSONObject: params, options: JSONSerialization.WritingOptions())
  } catch {
    print("bad things happened")
  }
}

extension Dictionary

func stringFromHttpParameters() -> String {
let parameterArray = self.map { (key, value) -> String in let percentEscapedKey = (key as!String).stringByAddingPercentEncodingForURLQueryValue()!

let percentEscapedValue = (value as! String).stringByAddingPercentEncodingForURLQueryValue()!}
return "\(percentEscapedKey)=\(percentEscapedValue)"}
return parameterArray.joined(separator: "&")}

Upvotes: -1

Diego Renau
Diego Renau

Reputation: 157

With Swift 3, let jsonData = try? JSONSerialization.data(withJSONObject: kParameters) didn't work fine for me, so i had to copy the AlamoFire solution...

let body2 = ["username": "[email protected]",
        "password": "111",
        "client_secret":"7E",
        "grant_type":"password"]

let data : Data = query(body2).data(using: .utf8, allowLossyConversion: false)!
var request : URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type");
request.setValue(NSLocalizedString("lang", comment: ""), forHTTPHeaderField:"Accept-Language");
request.httpBody = data 

do {...}

}

public func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
        var components: [(String, String)] = []

        if let dictionary = value as? [String: Any] {
            for (nestedKey, value) in dictionary {
                components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
            }
        } else if let array = value as? [Any] {
            for value in array {
                components += queryComponents(fromKey: "\(key)[]", value: value)
            }
        } else if let value = value as? NSNumber {
            if value.isBool {
                components.append((escape(key), escape((value.boolValue ? "1" : "0"))))
            } else {
                components.append((escape(key), escape("\(value)")))
            }
        } else if let bool = value as? Bool {
            components.append((escape(key), escape((bool ? "1" : "0"))))
        } else {
            components.append((escape(key), escape("\(value)")))
        }

        return components
    }


    public func escape(_ string: String) -> String {
        let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
        let subDelimitersToEncode = "!$&'()*+,;="

        var allowedCharacterSet = CharacterSet.urlQueryAllowed
        allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")

        var escaped = ""

        if #available(iOS 8.3, *) {
            escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
        } else {
            let batchSize = 50
            var index = string.startIndex

            while index != string.endIndex {
                let startIndex = index
                let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
                let range = startIndex..<endIndex

                let substring = string.substring(with: range)

                escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? substring

                index = endIndex
            }
        }
        return escaped
    }

And one extension:

extension NSNumber {
fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) }}

This is temporary, It has to be a better solution...

Hope it help...

Upvotes: 2

Muarl
Muarl

Reputation: 1987

@fatihyildizhan

not enough reputation to directly comment your answer therefore this answer.

Swift 1.2

let myParams = "username=user1&password=12345"
let postData = myParams.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)
let postLength = String(format: "%d", postData!.length)

var myRequest = NSMutableURLRequest(URL: self.url)
myRequest.HTTPMethod = "POST"
myRequest.setValue(postLength, forHTTPHeaderField: "Content-Length")
myRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
myRequest.HTTPBody = postData

var response: AutoreleasingUnsafeMutablePointer<NSURLResponse?> = nil

This code above just works fine in my case.

Upvotes: 7

Related Questions