forked from mike123789-dev/APIKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRequest.swift
More file actions
153 lines (121 loc) · 5.81 KB
/
Copy pathRequest.swift
File metadata and controls
153 lines (121 loc) · 5.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import Foundation
/// `Request` protocol represents a request for Web API.
/// Following 5 items must be implemented.
/// - `typealias Response`
/// - `var baseURL: URL`
/// - `var method: HTTPMethod`
/// - `var path: String`
/// - `func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response`
public protocol Request {
/// The response type associated with the request type.
associatedtype Response
/// The base URL.
var baseURL: URL { get }
/// The HTTP request method.
var method: HTTPMethod { get }
/// The path URL component.
var path: String { get }
/// The convenience property for `queryParameters` and `bodyParameters`. If the implementation of
/// `queryParameters` and `bodyParameters` are not provided, the values for them will be computed
/// from this property depending on `method`.
var parameters: Any? { get }
/// The actual parameters for the URL query. The values of this property will be escaped using `URLEncodedSerialization`.
/// If this property is not implemented and `method.prefersQueryParameter` is `true`, the value of this property
/// will be computed from `parameters`.
var queryParameters: [String: Any]? { get }
/// The actual parameters for the HTTP body. If this property is not implemented and `method.prefersQueryParameter` is `false`,
/// the value of this property will be computed from `parameters` using `JSONBodyParameters`.
var bodyParameters: BodyParameters? { get }
/// The HTTP header fields. In addition to fields defined in this property, `Accept` and `Content-Type`
/// fields will be added by `dataParser` and `bodyParameters`. If you define `Accept` and `Content-Type`
/// in this property, the values in this property are preferred.
var headerFields: [String: String] { get }
/// The parser object that states `Content-Type` to accept and parses response body.
var dataParser: DataParser { get }
/// Intercepts `URLRequest` which is created by `Request.buildURLRequest()`. If an error is
/// thrown in this method, the result of `Session.send()` turns `.failure(.requestError(error))`.
/// - Throws: `Error`
func intercept(urlRequest: URLRequest) throws -> URLRequest
/// Intercepts response `Any` and `HTTPURLResponse`. If an error is thrown in this method,
/// the result of `Session.send()` turns `.failure(.responseError(error))`.
/// The default implementation of this method is provided to throw `ResponseError.unacceptableStatusCode`
/// if the HTTP status code is not in `200..<300`.
/// - Throws: `Error`
func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any
/// Build `Response` instance from raw response object. This method is called after
/// `intercept(object:urlResponse:)` if it does not throw any error.
/// - Throws: `Error`
func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response
}
public extension Request {
var parameters: Any? {
return nil
}
var queryParameters: [String: Any]? {
guard let parameters = parameters as? [String: Any], method.prefersQueryParameters else {
return nil
}
return parameters
}
var bodyParameters: BodyParameters? {
guard let parameters = parameters, !method.prefersQueryParameters else {
return nil
}
return JSONBodyParameters(JSONObject: parameters)
}
var headerFields: [String: String] {
return [:]
}
var dataParser: DataParser {
return JSONDataParser(readingOptions: [])
}
func intercept(urlRequest: URLRequest) throws -> URLRequest {
return urlRequest
}
func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any {
guard 200..<300 ~= urlResponse.statusCode else {
throw ResponseError.unacceptableStatusCode(urlResponse.statusCode)
}
return object
}
/// Builds `URLRequest` from properties of `self`.
/// - Throws: `RequestError`, `Error`
func buildURLRequest() throws -> URLRequest {
let url = path.isEmpty ? baseURL : baseURL.appendingPathComponent(path)
guard var components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
throw RequestError.invalidBaseURL(baseURL)
}
var urlRequest = URLRequest(url: url)
if let queryParameters = queryParameters, !queryParameters.isEmpty {
components.percentEncodedQuery = URLEncodedSerialization.string(from: queryParameters)
}
if let bodyParameters = bodyParameters {
urlRequest.setValue(bodyParameters.contentType, forHTTPHeaderField: "Content-Type")
switch try bodyParameters.buildEntity() {
case .data(let data):
urlRequest.httpBody = data
case .inputStream(let inputStream):
urlRequest.httpBodyStream = inputStream
}
}
urlRequest.url = components.url
urlRequest.httpMethod = method.rawValue
urlRequest.setValue(dataParser.contentType, forHTTPHeaderField: "Accept")
headerFields.forEach { key, value in
urlRequest.setValue(value, forHTTPHeaderField: key)
}
return (try intercept(urlRequest: urlRequest) as URLRequest)
}
/// Builds `Response` from response `Data`.
/// - Throws: `ResponseError`, `Error`
func parse(data: Data, urlResponse: HTTPURLResponse) throws -> Response {
let parsedObject = try dataParser.parse(data: data)
let passedObject = try intercept(object: parsedObject, urlResponse: urlResponse)
return try response(from: passedObject, urlResponse: urlResponse)
}
}
public extension Request where Response == Void {
func response(from object: Any, urlResponse: HTTPURLResponse) throws {
return
}
}