Open kbrz opened 4 years ago
The same goes for email addresses with a + in which is a valid email address but does not work over the service. We cannot encode the + before the time because that results in it being double encoded.
Or supposed to pass handle this?
For everybody stumbling upon this:
considering this, our solution was to create custom encodings based on ParameterEncoding
protocol with percent escape.
In particular, I'm referring to form url encoding and url encoding.
class OpenAPICustomFormURLEncoding: ParameterEncoding {
func encode(_ urlRequest: URLRequest, with parameters: [String: Any]?) throws -> URLRequest {
var urlRequest = urlRequest
var requestBodyComponents = URLComponents()
requestBodyComponents.queryItems = APIHelper.mapValuesToQueryItemsEscaped(parameters ?? [:])
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = requestBodyComponents.query?.data(using: .utf8)
return urlRequest
}
}
where mapValuesToQueryItemsEscaped(_ source: [String: Any?]) -> [URLQueryItem]?
is a utils method added to a APIHelper
extension
/// Map passed values to percent escaped query items. This method is a copy / paste `mapValuesToQueryItems`
/// from `APIHelper`, adding escape through `escape` utils.
/// - Parameter source: keys - values to escape and used to create query params
/// - Returns: a `URLQueryItem` list of query items
public static func mapValuesToQueryItemsEscaped(_ source: [String: Any?]) -> [URLQueryItem]? {
let destination = source.filter { $0.value != nil }.reduce(into: [URLQueryItem]()) { result, item in
if let collection = item.value as? [Any?] {
collection.filter { $0 != nil }.map { "\($0!)" }.forEach { value in
result.append(URLQueryItem(name: escape(item.key), value: escape(value)))
}
} else if let value = item.value {
result.append(URLQueryItem(name: escape(item.key), value: escape("\(value)")))
}
}
if destination.isEmpty {
return nil
}
return destination
}
Finally escape(_ string: String) -> String
is another utils method added to the same extension
/// Returns a percent-escaped string following RFC 3986 for a query string key or value.
///
/// RFC 3986 states that the following characters are "reserved" characters.
///
/// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
/// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
///
/// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
/// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
/// should be percent-escaped in the query string.
///
/// - parameter string: The string to be percent-escaped.
///
/// - returns: The percent-escaped string.
public static 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)")
return string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
}
The encoding method was taken from Alamofire
parameter encoding implementation here.
From our side is perfectly working, consistent and is not depending from OpenAPI generated code.
Hope this helps.
Is your feature request related to a problem? Please describe.
When sending a date as URL query param and using ISO 8601 date formatter the resulting string may contain a plus ('+') sign. As per apple documentation this plus sign isn't percent encoded. Most of web servers will translate this plus sign into space and backend will receive invalid date format.
Describe the solution you'd like
Enhance current implementation to replace plus signs in date strings with percent encoded value (%2B).
Describe alternatives you've considered
Another feature request might be adding a possibility to specify multiple date formatter per request body/params and response.