Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
//
2
//  ParameterEncoder.swift
3
//
4
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
5
//
6
//  Permission is hereby granted, free of charge, to any person obtaining a copy
7
//  of this software and associated documentation files (the "Software"), to deal
8
//  in the Software without restriction, including without limitation the rights
9
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
//  copies of the Software, and to permit persons to whom the Software is
11
//  furnished to do so, subject to the following conditions:
12
//
13
//  The above copyright notice and this permission notice shall be included in
14
//  all copies or substantial portions of the Software.
15
//
16
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
//  THE SOFTWARE.
23
//
24
 
25
import Foundation
26
 
27
/// A type that can encode any `Encodable` type into a `URLRequest`.
28
public protocol ParameterEncoder {
29
    /// Encode the provided `Encodable` parameters into `request`.
30
    ///
31
    /// - Parameters:
32
    ///   - parameters: The `Encodable` parameter value.
33
    ///   - request:    The `URLRequest` into which to encode the parameters.
34
    ///
35
    /// - Returns:      A `URLRequest` with the result of the encoding.
36
    /// - Throws:       An `Error` when encoding fails. For Alamofire provided encoders, this will be an instance of
37
    ///                 `AFError.parameterEncoderFailed` with an associated `ParameterEncoderFailureReason`.
38
    func encode<Parameters: Encodable>(_ parameters: Parameters?, into request: URLRequest) throws -> URLRequest
39
}
40
 
41
/// A `ParameterEncoder` that encodes types as JSON body data.
42
///
43
/// If no `Content-Type` header is already set on the provided `URLRequest`s, it's set to `application/json`.
44
open class JSONParameterEncoder: ParameterEncoder {
45
    /// Returns an encoder with default parameters.
46
    public static var `default`: JSONParameterEncoder { JSONParameterEncoder() }
47
 
48
    /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.prettyPrinted`.
49
    public static var prettyPrinted: JSONParameterEncoder {
50
        let encoder = JSONEncoder()
51
        encoder.outputFormatting = .prettyPrinted
52
 
53
        return JSONParameterEncoder(encoder: encoder)
54
    }
55
 
56
    /// Returns an encoder with `JSONEncoder.outputFormatting` set to `.sortedKeys`.
57
    @available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
58
    public static var sortedKeys: JSONParameterEncoder {
59
        let encoder = JSONEncoder()
60
        encoder.outputFormatting = .sortedKeys
61
 
62
        return JSONParameterEncoder(encoder: encoder)
63
    }
64
 
65
    /// `JSONEncoder` used to encode parameters.
66
    public let encoder: JSONEncoder
67
 
68
    /// Creates an instance with the provided `JSONEncoder`.
69
    ///
70
    /// - Parameter encoder: The `JSONEncoder`. `JSONEncoder()` by default.
71
    public init(encoder: JSONEncoder = JSONEncoder()) {
72
        self.encoder = encoder
73
    }
74
 
75
    open func encode<Parameters: Encodable>(_ parameters: Parameters?,
76
                                            into request: URLRequest) throws -> URLRequest {
77
        guard let parameters = parameters else { return request }
78
 
79
        var request = request
80
 
81
        do {
82
            let data = try encoder.encode(parameters)
83
            request.httpBody = data
84
            if request.headers["Content-Type"] == nil {
85
                request.headers.update(.contentType("application/json"))
86
            }
87
        } catch {
88
            throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
89
        }
90
 
91
        return request
92
    }
93
}
94
 
95
#if swift(>=5.5)
96
extension ParameterEncoder where Self == JSONParameterEncoder {
97
    /// Provides a default `JSONParameterEncoder` instance.
98
    public static var json: JSONParameterEncoder { JSONParameterEncoder() }
99
 
100
    /// Creates a `JSONParameterEncoder` using the provided `JSONEncoder`.
101
    ///
102
    /// - Parameter encoder: `JSONEncoder` used to encode parameters. `JSONEncoder()` by default.
103
    /// - Returns:           The `JSONParameterEncoder`.
104
    public static func json(encoder: JSONEncoder = JSONEncoder()) -> JSONParameterEncoder {
105
        JSONParameterEncoder(encoder: encoder)
106
    }
107
}
108
#endif
109
 
110
/// A `ParameterEncoder` that encodes types as URL-encoded query strings to be set on the URL or as body data, depending
111
/// on the `Destination` set.
112
///
113
/// If no `Content-Type` header is already set on the provided `URLRequest`s, it will be set to
114
/// `application/x-www-form-urlencoded; charset=utf-8`.
115
///
116
/// Encoding behavior can be customized by passing an instance of `URLEncodedFormEncoder` to the initializer.
117
open class URLEncodedFormParameterEncoder: ParameterEncoder {
118
    /// Defines where the URL-encoded string should be set for each `URLRequest`.
119
    public enum Destination {
120
        /// Applies the encoded query string to any existing query string for `.get`, `.head`, and `.delete` request.
121
        /// Sets it to the `httpBody` for all other methods.
122
        case methodDependent
123
        /// Applies the encoded query string to any existing query string from the `URLRequest`.
124
        case queryString
125
        /// Applies the encoded query string to the `httpBody` of the `URLRequest`.
126
        case httpBody
127
 
128
        /// Determines whether the URL-encoded string should be applied to the `URLRequest`'s `url`.
129
        ///
130
        /// - Parameter method: The `HTTPMethod`.
131
        ///
132
        /// - Returns:          Whether the URL-encoded string should be applied to a `URL`.
133
        func encodesParametersInURL(for method: HTTPMethod) -> Bool {
134
            switch self {
135
            case .methodDependent: return [.get, .head, .delete].contains(method)
136
            case .queryString: return true
137
            case .httpBody: return false
138
            }
139
        }
140
    }
141
 
142
    /// Returns an encoder with default parameters.
143
    public static var `default`: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() }
144
 
145
    /// The `URLEncodedFormEncoder` to use.
146
    public let encoder: URLEncodedFormEncoder
147
 
148
    /// The `Destination` for the URL-encoded string.
149
    public let destination: Destination
150
 
151
    /// Creates an instance with the provided `URLEncodedFormEncoder` instance and `Destination` value.
152
    ///
153
    /// - Parameters:
154
    ///   - encoder:     The `URLEncodedFormEncoder`. `URLEncodedFormEncoder()` by default.
155
    ///   - destination: The `Destination`. `.methodDependent` by default.
156
    public init(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(), destination: Destination = .methodDependent) {
157
        self.encoder = encoder
158
        self.destination = destination
159
    }
160
 
161
    open func encode<Parameters: Encodable>(_ parameters: Parameters?,
162
                                            into request: URLRequest) throws -> URLRequest {
163
        guard let parameters = parameters else { return request }
164
 
165
        var request = request
166
 
167
        guard let url = request.url else {
168
            throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
169
        }
170
 
171
        guard let method = request.method else {
172
            let rawValue = request.method?.rawValue ?? "nil"
173
            throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.httpMethod(rawValue: rawValue)))
174
        }
175
 
176
        if destination.encodesParametersInURL(for: method),
177
           var components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
178
            let query: String = try Result<String, Error> { try encoder.encode(parameters) }
179
                .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get()
180
            let newQueryString = [components.percentEncodedQuery, query].compactMap { $0 }.joinedWithAmpersands()
181
            components.percentEncodedQuery = newQueryString.isEmpty ? nil : newQueryString
182
 
183
            guard let newURL = components.url else {
184
                throw AFError.parameterEncoderFailed(reason: .missingRequiredComponent(.url))
185
            }
186
 
187
            request.url = newURL
188
        } else {
189
            if request.headers["Content-Type"] == nil {
190
                request.headers.update(.contentType("application/x-www-form-urlencoded; charset=utf-8"))
191
            }
192
 
193
            request.httpBody = try Result<Data, Error> { try encoder.encode(parameters) }
194
                .mapError { AFError.parameterEncoderFailed(reason: .encoderFailed(error: $0)) }.get()
195
        }
196
 
197
        return request
198
    }
199
}
200
 
201
#if swift(>=5.5)
202
extension ParameterEncoder where Self == URLEncodedFormParameterEncoder {
203
    /// Provides a default `URLEncodedFormParameterEncoder` instance.
204
    public static var urlEncodedForm: URLEncodedFormParameterEncoder { URLEncodedFormParameterEncoder() }
205
 
206
    /// Creates a `URLEncodedFormParameterEncoder` with the provided encoder and destination.
207
    ///
208
    /// - Parameters:
209
    ///   - encoder:     `URLEncodedFormEncoder` used to encode the parameters. `URLEncodedFormEncoder()` by default.
210
    ///   - destination: `Destination` to which to encode the parameters. `.methodDependent` by default.
211
    /// - Returns:       The `URLEncodedFormParameterEncoder`.
212
    public static func urlEncodedForm(encoder: URLEncodedFormEncoder = URLEncodedFormEncoder(),
213
                                      destination: URLEncodedFormParameterEncoder.Destination = .methodDependent) -> URLEncodedFormParameterEncoder {
214
        URLEncodedFormParameterEncoder(encoder: encoder, destination: destination)
215
    }
216
}
217
#endif