Alamofire - 如何从 AFError 中获取 API 错误
Alamofire - How to get API error from AFError
在我正确实施 Alamofire 5 和处理自定义错误模型响应的过程中,我还没有找到有示例的可接受答案。
为了尽可能彻底,这是我的apiclient
class APIClient {
static let sessionManager: Session = {
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = 30
configuration.waitsForConnectivity = true
return Session(configuration: configuration, eventMonitors: [APILogger()])
}()
@discardableResult
private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:@escaping (Result<T, AFError>)->Void) -> DataRequest {
return sessionManager.request(route)
// .validate(statusCode: 200..<300) // This will kill the server side error response...
.responseDecodable (decoder: decoder){ (response: DataResponse<T, AFError>) in
completion(response.result)
}
}
static func login(username: String, password: String, completion:@escaping (Result<User, AFError>)->Void) {
performRequest(route: APIRouter.login(username: username, password: password), completion: completion)
}
}
我是这样用的
APIClient.login(username: "", password: "") { result in
debugPrint(result)
switch result {
case .success(let user):
debugPrint("__________SUCCESS__________")
case .failure(let error):
debugPrint("__________FAILURE__________")
debugPrint(error.localizedDescription)
}
}
我注意到,如果我使用 .validate()
,调用函数将收到一个失败消息,但响应数据丢失。环顾四周,注意到 and here 施放 underlyingError
但那是零。
服务器以我在调用函数级别需要的可解析错误模型进行响应。在apiclient级别反序列化JSON并return它作为失败返回到调用函数会更令人愉快。
{
"errorObject": {
"summary": "",
"details": [{
...
}]
}
}
更新
感谢@GIJoeCodes 的评论,我使用路由器实现了这个类似的解决方案。
class APIClient {
static let sessionManager: Session = {
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = 30
configuration.waitsForConnectivity = true
return Session(configuration: configuration, eventMonitors: [APILogger()])
}()
@discardableResult
private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:@escaping (_ response: T?, _ error: Error?)->Void) {
sessionManager.request(route)
.validate(statusCode: 200..<300) // This will kill the server side error response...
.validate(contentType: ["application/json"])
.responseJSON { response in
guard let data = response.data else { return }
do {
switch response.result {
case .success:
let object = try decoder.decode(T.self, from: data)
completion(object, nil)
case .failure:
let error = try decoder.decode(ErrorWrapper.self, from: data)
completion(nil, error.error)
}
} catch {
debugPrint(error)
}
}
}
// MARK: - Authentication
static func login(username: String, password: String, completion:@escaping (_ response: User?, _ error: Error?)->Void) {
performRequest(route: APIRouter.login(username: username, password: password), completion: completion)
}
}
这样调用
APIClient.login(username: "", password: "") { (user, error) in
if let error = error {
debugPrint("__________FAILURE__________")
debugPrint(error)
return
}
if let user = user {
debugPrint("__________SUCCESS__________")
debugPrint(user)
}
}
这就是我获取错误和自定义错误消息的方式。在验证中,我得到了 200..<300 响应之外的错误:
AF.request(
url,
method: .post,
parameters: json,
encoder: JSONParameterEncoder.prettyPrinted,
headers: headers
).validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseJSON { response in
switch response.result {
case .success(let result):
let json = JSON(result)
onSuccess()
case .failure(let error):
guard let data = response.data else { return }
do {
let json = try JSON(data: data)
let message = json["message"]
onError(message.rawValue as! String)
} catch {
print(error)
}
onError(error.localizedDescription)
}
debugPrint(response)
}
首先,如果您已经拥有 Decodable
模型,则无需使用 responseJSON
。您通过多次解码响应数据来做不必要的工作。使用 responseDecodable
并提供您的 Decodable
类型,在本例中为您的通用 T
。 responseDecodable(of: T)
.
其次,将您期望的 Decodable
类型包装在枚举中是解决此问题的典型方法。例如:
enum APIResponse<T: Decodable> {
case success(T)
case failure(APIError)
}
然后实现 APIResponse
的 Decodable
来尝试解析成功的类型或 APIError
(有很多这样的例子)。然后,您可以使用 responseDecodable(of: APIResponse<T>.self)
.
解析您的响应
在我正确实施 Alamofire 5 和处理自定义错误模型响应的过程中,我还没有找到有示例的可接受答案。
为了尽可能彻底,这是我的apiclient
class APIClient {
static let sessionManager: Session = {
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = 30
configuration.waitsForConnectivity = true
return Session(configuration: configuration, eventMonitors: [APILogger()])
}()
@discardableResult
private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:@escaping (Result<T, AFError>)->Void) -> DataRequest {
return sessionManager.request(route)
// .validate(statusCode: 200..<300) // This will kill the server side error response...
.responseDecodable (decoder: decoder){ (response: DataResponse<T, AFError>) in
completion(response.result)
}
}
static func login(username: String, password: String, completion:@escaping (Result<User, AFError>)->Void) {
performRequest(route: APIRouter.login(username: username, password: password), completion: completion)
}
}
我是这样用的
APIClient.login(username: "", password: "") { result in
debugPrint(result)
switch result {
case .success(let user):
debugPrint("__________SUCCESS__________")
case .failure(let error):
debugPrint("__________FAILURE__________")
debugPrint(error.localizedDescription)
}
}
我注意到,如果我使用 .validate()
,调用函数将收到一个失败消息,但响应数据丢失。环顾四周,注意到 underlyingError
但那是零。
服务器以我在调用函数级别需要的可解析错误模型进行响应。在apiclient级别反序列化JSON并return它作为失败返回到调用函数会更令人愉快。
{
"errorObject": {
"summary": "",
"details": [{
...
}]
}
}
更新
感谢@GIJoeCodes 的评论,我使用路由器实现了这个类似的解决方案。
class APIClient {
static let sessionManager: Session = {
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = 30
configuration.waitsForConnectivity = true
return Session(configuration: configuration, eventMonitors: [APILogger()])
}()
@discardableResult
private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:@escaping (_ response: T?, _ error: Error?)->Void) {
sessionManager.request(route)
.validate(statusCode: 200..<300) // This will kill the server side error response...
.validate(contentType: ["application/json"])
.responseJSON { response in
guard let data = response.data else { return }
do {
switch response.result {
case .success:
let object = try decoder.decode(T.self, from: data)
completion(object, nil)
case .failure:
let error = try decoder.decode(ErrorWrapper.self, from: data)
completion(nil, error.error)
}
} catch {
debugPrint(error)
}
}
}
// MARK: - Authentication
static func login(username: String, password: String, completion:@escaping (_ response: User?, _ error: Error?)->Void) {
performRequest(route: APIRouter.login(username: username, password: password), completion: completion)
}
}
这样调用
APIClient.login(username: "", password: "") { (user, error) in
if let error = error {
debugPrint("__________FAILURE__________")
debugPrint(error)
return
}
if let user = user {
debugPrint("__________SUCCESS__________")
debugPrint(user)
}
}
这就是我获取错误和自定义错误消息的方式。在验证中,我得到了 200..<300 响应之外的错误:
AF.request(
url,
method: .post,
parameters: json,
encoder: JSONParameterEncoder.prettyPrinted,
headers: headers
).validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseJSON { response in
switch response.result {
case .success(let result):
let json = JSON(result)
onSuccess()
case .failure(let error):
guard let data = response.data else { return }
do {
let json = try JSON(data: data)
let message = json["message"]
onError(message.rawValue as! String)
} catch {
print(error)
}
onError(error.localizedDescription)
}
debugPrint(response)
}
首先,如果您已经拥有 Decodable
模型,则无需使用 responseJSON
。您通过多次解码响应数据来做不必要的工作。使用 responseDecodable
并提供您的 Decodable
类型,在本例中为您的通用 T
。 responseDecodable(of: T)
.
其次,将您期望的 Decodable
类型包装在枚举中是解决此问题的典型方法。例如:
enum APIResponse<T: Decodable> {
case success(T)
case failure(APIError)
}
然后实现 APIResponse
的 Decodable
来尝试解析成功的类型或 APIError
(有很多这样的例子)。然后,您可以使用 responseDecodable(of: APIResponse<T>.self)
.