Alamofire post cp1251

alamofire
ios

#1

Пытаюсь отправить данные на сервер используя Alamofire:

let login = "admin"
let password = "password"
let parameters = ["login": login!,
  "password": password!,
  "tickid": tickID,
  "newticktext": comment
] as [String : Any]
let url = "https://www.myserver.ru/?addcomment"                
Alamofire.request(url, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: nil).response { response in}

Но сервер принимает в кодировке cp1251 и в итоге появляется ничитаемая белиберда.
Как правильно отправить данные в этой кодировке?


#2

Пробую вот так закодировать:

var parameters : [String : Any] = [
  "newticktext": comment.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""
]

но вместо читаемого текста “Тест” получаю

%D0%A2%D0%B5%D1%81%D1%82

Если просто указать

var parameters : [String : Any] = [
  "newticktext": comment
]

то выдает

Тест


#3

выдаёт на сервере такое?


#4

Да, т.е. комментарий добавился, но на страничке с остальными комментариями выдает это. Через форму на сайте нормально все отправляется.


#5

Идет 2018 год, а люди все еще без utf8.
Не проще кодировку на сервере поменять?


#6

Проще на сервере кодировку поменять и ответы слать в JSON. Но админам мягко говоря пофиг, приложение я пишу для себя.
По-этому только парсинг, только кодировка cp1251…


#7

Судя по дате вопроса, возможно это были вы, если нет, тогда посмотрите


#8

Да это я. Еще на Stack Overflow задал вопрос, но пока нет решения.


#9

Решение помог найти @doublench21 на toster.ru.
К сожалению Alamofire использует жесткую привязку к utf8, о чем говорят ребята со StackOverflow, и советуют на сервере использовать именно его. Но у меня нет возможности изменить что-либо на сервере. Остается кодить…
Суть решения - использовать URLSession. Тут тоже не без сюрпризов.
Сначала добавляем (вне класса, можно отдельным файлом) следующий код:

protocol URLQueryParameterStringConvertible {
  var queryParameters: String {get}
}

extension Dictionary : URLQueryParameterStringConvertible {
  var queryParameters: String {
    var parts: [String] = []
    for (key, value) in self {
      let part = String(format: "%@=%@",
                        String(describing: key).win1251Encoded,
                        String(describing: value).win1251Encoded)
      parts.append(part as String)
    }
    return parts.joined(separator: "&")
  }
  
}

extension URL {
  func appendingQueryParameters(_ parametersDictionary : [String: String]) -> URL {
    let URLString : String = String(format: "%@?%@", self.absoluteString, parametersDictionary.queryParameters)
    return URL(string: URLString)!
  }
}

// Раньше Swift(Foundation) позволял любую строку закодировать в url-encode в любой кодировке. 
// Сейчас же де факто это можно лишь сделать в utf-8. 
// Видимо Apple аргументирует это тем,  что utf-8 - это стандарт в вебе. 

// Да всех остальных случаев нужно писать такую функцию самому. 
// Ниже кодировка в url-encode в windows-1251

extension CharacterSet {
  static let rfc3986Unreserved = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~=&+")
}

extension String.Encoding {
  static let win1251 = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.windowsCyrillic.rawValue)))
}

extension String {
  func addingPercentEncoding(withAllowedCharacters characterSet: CharacterSet, using encoding: String.Encoding) -> String {
    let stringData = self.data(using: encoding, allowLossyConversion: true) ?? Data()
    let percentEscaped = stringData.map {byte->String in
      if characterSet.contains(UnicodeScalar(byte)) {
        return String(UnicodeScalar(byte))
      } else if byte == UInt8(ascii: " ") {
        return "+"
      } else {
        return String(format: "%%%02X", byte)
      }
      }.joined()
    return percentEscaped
  }
  
  var win1251Encoded: String {
    return self.addingPercentEncoding(withAllowedCharacters: .rfc3986Unreserved,  using: . win1251)
  }
}

Затем в коде, где необходимо обратиться к серверу в кодировке cp1251:

let session = URLSession.shared
var URL = URL(string: "https://greatcomments.server/add")!
var request = URLRequest(url: URL)
request.httpMethod = "POST"
    
 // Headers
request.addValue("application/x-www-form-urlencoded; charset=windows-1251", forHTTPHeaderField: "Content-Type")

// Form URL-Encoded Body 
let bodyParameters = [
     "comment": "Проверка",
]
let bodyString = bodyParameters.queryParameters
request.httpBody = bodyString.data(using: . win1251, allowLossyConversion: true)

let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) -> Void in
      if (error == nil) {
        // Success
        let statusCode = (response as! HTTPURLResponse).statusCode
        print("URL Session Task Succeeded: HTTP \(statusCode)")
      }
      else {
        // Failure
        print("URL Session Task Failed: %@", error!.localizedDescription);
      }
    })
task.resume()
session.finishTasksAndInvalidate()

Можно конечно поискать в самом фреймворке Alamofire все упоминания “.utf8” и заменить их на “.cp1251”, но не уверен, что это сработает.