【问题标题】:POST request using application/x-www-form-urlencodedPOST 请求使用 application/x-www-form-urlencoded
【发布时间】:2014-03-30 14:43:19
【问题描述】:

后端开发人员在 POST 请求中给出了这些说明:

  1. 路由:{url}/{app_name/{controller}/{action}
  2. 控制器和操作应在小型大写字母上。
  3. API 测试链接:http:********** ******
  4. 请求应使用 POST 方法。
  5. 参数应通过请求内容主体 (FormUrlEncodedContent) 传递。
  6. 参数应为 json 格式。
  7. 参数是按键敏感的。

对协议中的数字 5 没有经验,我搜索并以我的代码结束。

-(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];
        }

    }];

}

我传递了一个格式化为 JSON 的字典。他同意我能够传递正确的数据。而且我能够连接到 API,但是当我尝试发送数据进行注册时,它总是返回“FAILED”。连接没有问题,但我传输数据失败。

这里使用相同API的android开发者没有问题,但由于他不熟悉iOS,所以无法帮助我。

我错过了什么?

【问题讨论】:

  • 在某些情况下,我遇到了与 JSON 参数的 ORDER 相关的问题。由于 NSDictionary 是无序的,因此当您将其转换为 JSON 时,参数的顺序可能与您在声明它时使用的顺序不同。尝试手动设置 JSON 字符串并检查它是否有效。如果问题是这个,就得使用有序字典(在GitHub上搜索,有很多实现)

标签: ios iphone json httprequest


【解决方案1】:

试试这个代码

目标 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];

斯威夫特 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()

斯威夫特 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.request("Add Your URL Here",method: .post, parameters: ["CategoryId": "15"])
        .validate(contentType: ["application/x-www-form-urlencoded"])
        .responseJSON { (response) in

            print(response.result.value)

    }

希望这段代码对你有用。

【讨论】:

  • 谢谢..它对我有用..!!不知道为什么答案没有被标记为正确。
  • Swift 3 代码示例错误。它根本没有做 application/x-www-form-urlencoded,它正在发送 JSON。
  • 请为 Alamofire 添加示例。谢谢
  • @AwaisFayyaz 当然,我很快就会这样做。谢谢
  • @AwaisFayyaz 根据您的要求,我在答案中添加了 Alamofire 示例。
【解决方案2】:

是否可以将此代码转换为 swift ?我已经尝试过但无法处理。也许这个代码块可以帮助你。谢谢。

    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!

【讨论】:

    【解决方案3】:

    @fatihyildizhan

    没有足够的声誉直接评论你的答案,因此这个答案。

    斯威夫特 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
    

    上面的代码在我的情况下可以正常工作。

    【讨论】:

    【解决方案4】:
    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")
      }
    }
    

    扩展词典

    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: "&")}
    

    【讨论】:

      【解决方案5】:

      使用 Swift 3,让 jsonData = 试试? JSONSerialization.data(withJSONObject: kParameters) 对我来说效果不佳,所以我不得不复制 AlamoFire 解决方案...

      let body2 = ["username": "au@gmail.com",
              "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
          }
      

      还有一个扩展:

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

      这是暂时的,它必须是一个更好的解决方案......

      希望对您有所帮助...

      【讨论】:

      • 在这一行 let data : Data = query(body2).data(using: .utf8, allowLossyConversion: false)! 什么是 query() ?我没有在你的代码中找到它!
      • 此答案未正确对参数进行 url 编码,因此服务器可能会拒绝包含保留字符的数据。请参阅此处的规范:url.spec.whatwg.org/#urlencoded-serializing
      【解决方案6】:

      斯威夫特 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
                    }
                  }
      

      这对我有用,这里是源链接https://gist.github.com/HomerJSimpson/80c95f0424b8e9718a40

      【讨论】:

      • 此答案正确处理百分比编码,使用此处指定的机制:url.spec.whatwg.org/#urlencoded-serializing 其他可用答案中的 url 编码(通常缺少,如果存在,不正确)这个问题没有涵盖正确的字符。使用这些选项时,正文中更复杂的数据将被错误编码,因此接收服务器将无法理解有效负载。 URLRequest 没有内置任何东西来处理这个问题,这有点令人惊讶。
      【解决方案7】:

      Swift 确实提供了 URL-%-encoding 的功能,但正如@nolanw 在第 1 条评论中所指出的那样,它并不完全匹配。对于原始问题的第 5 步,一旦您拥有某种结构的键值对,这里有一个简短的编码替代方案(Swift 4.2):

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

      将其粘贴到 Xcode 游乐场,然后添加 print(httpBodyString!)。在输出中,您将看到:

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

      注意:这是用于基本表单值的百分比编码集(即不是二进制数据,也不是多部分)

      【讨论】:

      • 这是个好主意,但与spec for application/x-www-form-urlencoded serializing 不太匹配;空格应编码为+,但URLComponents.percentEncodedQuery 空格应编码为%20。这可能不会对您的用例产生影响,但我认为值得指出以防它影响其他人的用例!
      【解决方案8】:

      将字典参数解析为字符串:

      -(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];
      

      }

      并使用它:

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

      【讨论】:

        【解决方案9】:

        斯威夫特 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 
        

        【讨论】:

        • 需要删除最后一个 '&' 字符:let jsonString = json.reduce("") { "\($0)\($1.0)=\($1.1)&amp;" }.dropLast()
        【解决方案10】:

        此版本处理参数编码和用“+”替换空格。

        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()
        

        【讨论】:

        • 此答案正确处理百分比编码,使用此处指定的机制:url.spec.whatwg.org/#urlencoded-serializing 此问题的其他可用答案中的 url 编码(通常缺少,如果存在,不正确)不涵盖正确的人物。使用这些选项时,正文中更复杂的数据将被错误编码,因此接收服务器将无法理解有效负载。 URLRequest 没有内置任何东西来处理这个问题,这有点令人惊讶。
        • AFAICT 是这里唯一合适的答案。虽然作为 [String: String] 的扩展会更好。您可以使用带有反斜杠的""",以避免在允许的字符变量中出现丑陋的+
        【解决方案11】:

        从 Swift 5 开始,以下代码已经过测试,可以在今天运行。

        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
        

        【讨论】:

          猜你喜欢
          • 2015-06-18
          • 1970-01-01
          • 1970-01-01
          • 2018-03-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-04
          • 1970-01-01
          相关资源
          最近更新 更多