Alamofire 响应为零
Alamofire response is nil
我遇到了 Alamofire 请求和拦截器的问题。
我的服务器有两种类型的响应 -> 成功响应是一种对象,错误响应始终是错误消息。
我创建了一个示例代码来创建一个非常自己的函数来解码两种似乎运行良好的响应类型。
调用 Alamofire adapt 函数后出现问题(adapt 为每个请求添加一个 cookie)-> 在我的 .responseTwoDecodable 中,我总是得到 nil 响应,但状态代码为 200,一切正常,服务器returns 对象,但 alamofire 忽略了它。
这是我对每个请求的代码:
func request<T: Decodable>(
_ url: String,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
decoder: JSONDecoder = JSONDecoder(),
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil
) -> Future<T, ServerError> {
return Future({ promise in
AF.request(
url,
method: method,
parameters: parameters,
encoding: JSONEncoding.default,
headers: headers
interceptor: interceptor ?? self
)
.validate(statusCode: [200, 201, 204, 401]
.responseTwoDecodable(of: T.self) { response in
switch response {
case .success(let value):
promise(.success(value))
case .failure(let error):
promise(.failure(error))
}
}
})
}
这里是适配函数:
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, ServerError>) -> Void) {
let request = urlRequest
if let accessToken = UserDefaults.standard.string(forKey: Constants.accessToken), let refreshToken = UserDefaults.standard.string(forKey: Constants.refreshToken) {
let access = [
HTTPCookiePropertyKey.domain: Constants.cookieDomain,
HTTPCookiePropertyKey.path: Constants.cookieAccessPath,
HTTPCookiePropertyKey.name: Constants.accessToken,
HTTPCookiePropertyKey.value: accessToken
]
let refresh = [
HTTPCookiePropertyKey.domain: Constants.cookieDomain,
HTTPCookiePropertyKey.path: Constants.cookieRefreshPath,
HTTPCookiePropertyKey.name: Constants.refreshToken,
HTTPCookiePropertyKey.value: refreshToken
]
if let accessCookie = HTTPCookie(properties: access), let refreshCookie = HTTPCookie(properties: refresh) {
AF.session.configuration.httpCookieStorage?.setCookie(accessCookie)
AF.session.configuration.httpCookieStorage?.setCookie(refreshCookie)
completion(.success(request))
}
}
completion(.success(request))
}
这是我的两个解码器的解码代码:
struct ErrorMessage: Error, Decodable {
let message: String
}
struct ServerError: Error {
var message: String
var code: ServerErrorCodes
let args: [String]?
}
enum ServerErrorCodes: Int {
case unauthorized = 401
case forbidden = 403
case internalServerError = 500
case notFound = 404
case conflict = 409
case unknown
case emptyResponse
case unserialized
case userInput
case coreData
}
final class TwoDecodableResponseSerializer<T: Decodable>: ResponseSerializer {
lazy var decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return decoder
}()
private lazy var successSerializer = DecodableResponseSerializer<T>(decoder: decoder)
private lazy var errorSerializer = DecodableResponseSerializer<ErrorMessage>(decoder: decoder)
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Result<T, ServerError> {
guard error == nil else { return .failure(ServerError(message: "Unknown error", code: .unknown, args: [])) }
guard let response = response else { return .failure(ServerError(message: "Empty response", code: .emptyResponse, args: [])) } // HERE I AM GETTING A NIL AS A RESPONSE, BUT SERVER RESPONDED WITH CORRECT BODY AND 200 AS STATUS CODE
do {
print(response.debugDescription)
if response.statusCode < 200 || response.statusCode >= 300 {
let serverMessage = try errorSerializer.serialize(request: request, response: response, data: data, error: nil)
let responseCode: ServerErrorCodes
if let code = ServerErrorCodes(rawValue: response.statusCode) {
responseCode = code
} else {
responseCode = .unknown
}
let result = ServerError(message: serverMessage.message, code: responseCode, args: nil)
return .failure(result)
} else {
let result = try successSerializer.serialize(request: request, response: response, data: data, error: nil)
return .success(result)
}
} catch(let err) {
return .failure(ServerError(message: "Could not serialize body", code: .unserialized, args: [String(data: data!, encoding: .utf8)!, err.localizedDescription]))
}
}
}
extension DataRequest {
@discardableResult func responseTwoDecodable<T: Decodable>(queue: DispatchQueue = DispatchQueue.global(qos: .userInitiated), of t: T.Type, completionHandler: @escaping (Result<T, ServerError>) -> Void) -> Self {
return response(queue: .main, responseSerializer: TwoDecodableResponseSerializer<T>()) { response in
switch response.result {
case .success(let result):
completionHandler(result)
case .failure(let error):
completionHandler(.failure(ServerError(message: "Other error", code: .unknown, args: [error.localizedDescription])))
}
}
}
}
我是 Alamofire 的新手,所以如果可以用更好的方式完成某些事情,我将感谢您的帮助或分享您的想法!有谁知道为什么会发生这种情况?谢谢!
已解决:我完全删除了实现 .responseTwoDecodable
的代码并编写了自己的解决方案:
func createError(response: HTTPURLResponse?, error: AFError, data: Data?) -> ServerError {
guard let serverResponse = response else {
return .init(message: "Unknown error", code: .unknown, args: [error.localizedDescription])
}
guard let serverData = data else {
return .init(message: "Empty response", code: .emptyResponse, args: [error.localizedDescription])
}
let responseCode: ServerErrorCodes
if let code = ServerErrorCodes(rawValue: serverResponse.statusCode) {
responseCode = code
} else {
responseCode = .unknown
}
do {
let serverMessage = try JSONDecoder().decode(ErrorMessage.self, from: serverData)
return .init(message: serverMessage.message, code: responseCode, args: [])
} catch (let error) {
return .init(message: "Body not serializable", code: .unserialized, args: [String(data: serverData, encoding: .utf8) ?? "", error.localizedDescription])
}
}
并这样称呼它:
.responseDecodable(completionHandler: { (response: DataResponse<T, AFError>) in
switch response.result {
case .success(let value):
promise(.success(value))
case .failure(let error):
promise(.failure(self.createError(response: response.response, error: error, data: response.data)))
}
})
使用此代码,我能够按预期解码来自服务器的所有错误。
我遇到了 Alamofire 请求和拦截器的问题。
我的服务器有两种类型的响应 -> 成功响应是一种对象,错误响应始终是错误消息。 我创建了一个示例代码来创建一个非常自己的函数来解码两种似乎运行良好的响应类型。
调用 Alamofire adapt 函数后出现问题(adapt 为每个请求添加一个 cookie)-> 在我的 .responseTwoDecodable 中,我总是得到 nil 响应,但状态代码为 200,一切正常,服务器returns 对象,但 alamofire 忽略了它。
这是我对每个请求的代码:
func request<T: Decodable>(
_ url: String,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
decoder: JSONDecoder = JSONDecoder(),
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil
) -> Future<T, ServerError> {
return Future({ promise in
AF.request(
url,
method: method,
parameters: parameters,
encoding: JSONEncoding.default,
headers: headers
interceptor: interceptor ?? self
)
.validate(statusCode: [200, 201, 204, 401]
.responseTwoDecodable(of: T.self) { response in
switch response {
case .success(let value):
promise(.success(value))
case .failure(let error):
promise(.failure(error))
}
}
})
}
这里是适配函数:
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, ServerError>) -> Void) {
let request = urlRequest
if let accessToken = UserDefaults.standard.string(forKey: Constants.accessToken), let refreshToken = UserDefaults.standard.string(forKey: Constants.refreshToken) {
let access = [
HTTPCookiePropertyKey.domain: Constants.cookieDomain,
HTTPCookiePropertyKey.path: Constants.cookieAccessPath,
HTTPCookiePropertyKey.name: Constants.accessToken,
HTTPCookiePropertyKey.value: accessToken
]
let refresh = [
HTTPCookiePropertyKey.domain: Constants.cookieDomain,
HTTPCookiePropertyKey.path: Constants.cookieRefreshPath,
HTTPCookiePropertyKey.name: Constants.refreshToken,
HTTPCookiePropertyKey.value: refreshToken
]
if let accessCookie = HTTPCookie(properties: access), let refreshCookie = HTTPCookie(properties: refresh) {
AF.session.configuration.httpCookieStorage?.setCookie(accessCookie)
AF.session.configuration.httpCookieStorage?.setCookie(refreshCookie)
completion(.success(request))
}
}
completion(.success(request))
}
这是我的两个解码器的解码代码:
struct ErrorMessage: Error, Decodable {
let message: String
}
struct ServerError: Error {
var message: String
var code: ServerErrorCodes
let args: [String]?
}
enum ServerErrorCodes: Int {
case unauthorized = 401
case forbidden = 403
case internalServerError = 500
case notFound = 404
case conflict = 409
case unknown
case emptyResponse
case unserialized
case userInput
case coreData
}
final class TwoDecodableResponseSerializer<T: Decodable>: ResponseSerializer {
lazy var decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return decoder
}()
private lazy var successSerializer = DecodableResponseSerializer<T>(decoder: decoder)
private lazy var errorSerializer = DecodableResponseSerializer<ErrorMessage>(decoder: decoder)
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Result<T, ServerError> {
guard error == nil else { return .failure(ServerError(message: "Unknown error", code: .unknown, args: [])) }
guard let response = response else { return .failure(ServerError(message: "Empty response", code: .emptyResponse, args: [])) } // HERE I AM GETTING A NIL AS A RESPONSE, BUT SERVER RESPONDED WITH CORRECT BODY AND 200 AS STATUS CODE
do {
print(response.debugDescription)
if response.statusCode < 200 || response.statusCode >= 300 {
let serverMessage = try errorSerializer.serialize(request: request, response: response, data: data, error: nil)
let responseCode: ServerErrorCodes
if let code = ServerErrorCodes(rawValue: response.statusCode) {
responseCode = code
} else {
responseCode = .unknown
}
let result = ServerError(message: serverMessage.message, code: responseCode, args: nil)
return .failure(result)
} else {
let result = try successSerializer.serialize(request: request, response: response, data: data, error: nil)
return .success(result)
}
} catch(let err) {
return .failure(ServerError(message: "Could not serialize body", code: .unserialized, args: [String(data: data!, encoding: .utf8)!, err.localizedDescription]))
}
}
}
extension DataRequest {
@discardableResult func responseTwoDecodable<T: Decodable>(queue: DispatchQueue = DispatchQueue.global(qos: .userInitiated), of t: T.Type, completionHandler: @escaping (Result<T, ServerError>) -> Void) -> Self {
return response(queue: .main, responseSerializer: TwoDecodableResponseSerializer<T>()) { response in
switch response.result {
case .success(let result):
completionHandler(result)
case .failure(let error):
completionHandler(.failure(ServerError(message: "Other error", code: .unknown, args: [error.localizedDescription])))
}
}
}
}
我是 Alamofire 的新手,所以如果可以用更好的方式完成某些事情,我将感谢您的帮助或分享您的想法!有谁知道为什么会发生这种情况?谢谢!
已解决:我完全删除了实现 .responseTwoDecodable
的代码并编写了自己的解决方案:
func createError(response: HTTPURLResponse?, error: AFError, data: Data?) -> ServerError {
guard let serverResponse = response else {
return .init(message: "Unknown error", code: .unknown, args: [error.localizedDescription])
}
guard let serverData = data else {
return .init(message: "Empty response", code: .emptyResponse, args: [error.localizedDescription])
}
let responseCode: ServerErrorCodes
if let code = ServerErrorCodes(rawValue: serverResponse.statusCode) {
responseCode = code
} else {
responseCode = .unknown
}
do {
let serverMessage = try JSONDecoder().decode(ErrorMessage.self, from: serverData)
return .init(message: serverMessage.message, code: responseCode, args: [])
} catch (let error) {
return .init(message: "Body not serializable", code: .unserialized, args: [String(data: serverData, encoding: .utf8) ?? "", error.localizedDescription])
}
}
并这样称呼它:
.responseDecodable(completionHandler: { (response: DataResponse<T, AFError>) in
switch response.result {
case .success(let value):
promise(.success(value))
case .failure(let error):
promise(.failure(self.createError(response: response.response, error: error, data: response.data)))
}
})
使用此代码,我能够按预期解码来自服务器的所有错误。