如何阻止 Alamofire 对 URL 参数进行编码

Mic*_*kov 6 ios swift alamofire

我有一个这样的网址

https://www.mysite/v1/search?N=4249847587+1798040122

我像那样使用 Alamofire

Almofire.request(.GET, "https://www.mysite/v1/search", parameters: ["N","4249847587+1798040122"], encoding: .URL)
Run Code Online (Sandbox Code Playgroud)

记录请求,我收到

https://www.mysite/v1/search?N=4249847587%2B1798040122
Run Code Online (Sandbox Code Playgroud)

IE

“%2B”代替“+”

但是,我需要留在

“+”

如何使用 Alamofire 避免这种编码?

小智 4

一般来说,@rmaddy 的评论是正确的。但我们可以关闭这种编码作为一些有趣的练习。

为此,我们需要使用自定义编码器。Alamofire 支持任何实现ParameterEncoding协议而不是encoding: .URL.

因此,我们可以使用原始 Alamofire 代码库中的一些复制粘贴代码并创建自定义编码器

public struct NOURLEncoding: ParameterEncoding {

    //protocol implementation
    public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()

        guard let parameters = parameters else { return urlRequest }

        if HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET") != nil {
            guard let url = urlRequest.url else {
                throw AFError.parameterEncodingFailed(reason: .missingURL)
            }

            if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
                let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
                urlComponents.percentEncodedQuery = percentEncodedQuery
                urlRequest.url = urlComponents.url
            }
        }

        return urlRequest
    }

    //append query parameters 
    private func query(_ parameters: [String: Any]) -> String {
        var components: [(String, String)] = []

        for key in parameters.keys.sorted(by: <) {
            let value = parameters[key]!
            components += queryComponents(fromKey: key, value: value)
        }

        return components.map { "\($0)=\($1)" }.joined(separator: "&")
    }

    //Alamofire logic for query components handling
    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
    }

    //escaping function where we can select symbols which we want to escape 
    //(I just removed + for example)
    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 = ""

        escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string

        return escaped
    }

} 

extension NSNumber {
    fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) }
}
Run Code Online (Sandbox Code Playgroud)

我不知道为什么它有用。但可以轻松添加自定义编码。现在您可以通过我们的新编码器使用 Alamofire 请求

Alamofire.request("http://www.mysite/v1/search", method: .get, parameters: ["N": "4249847587+1798040122"], encoding: NOURLEncoding())
Run Code Online (Sandbox Code Playgroud)