如何解码 Alamofire 5 中的错误正文?
How to decode the body of an error in Alamofire 5?
我正在尝试将我的项目从 Alamofire 4.9 迁移到 5.3,但我很难处理错误。我想尽可能多地使用 Decodable
,但是当一切顺利时我的 API 端点 return 一个 JSON 结构,当一切顺利时另一个 JSON 结构有一个错误,所有端点上的所有错误都相同。我代码中对应的Codable
是ApiError
.
我想创建一个自定义响应序列化程序,它可以给我一个 Result<T, ApiError>
而不是默认的 Result<T, AFError>
。我发现 this article 似乎解释了一般过程,但其中的代码无法编译。
如何创建这样的自定义 ResponseSerializer
?
ResponseSerializer
s 有一个要求。在很大程度上,您可以只复制现有的序列化程序。例如,如果您想解析 CSV(不检查响应):
struct CommaDelimitedSerializer: ResponseSerializer {
func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> [String] {
// Call the existing StringResponseSerializer to get many behaviors automatically.
let string = try StringResponseSerializer().serialize(request: request,
response: response,
data: data,
error: error)
return Array(string.split(separator: ","))
}
}
您可以在 Alamofire's documentation 阅读更多内容。
我最终让它与以下 ResponseSerializer 一起工作:
struct APIError: Error, Decodable {
let message: String
let code: String
let args: [String]
}
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<APIError>(decoder: decoder)
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Result<T, APIError> {
guard error == nil else { return .failure(APIError(message: "Unknown error", code: "unknown", args: [])) }
guard let response = response else { return .failure(APIError(message: "Empty response", code: "empty_response", args: [])) }
do {
if response.statusCode < 200 || response.statusCode >= 300 {
let result = try errorSerializer.serialize(request: request, response: response, data: data, error: 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(APIError(message: "Could not serialize body", code: "unserializable_body", 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, APIError>) -> 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(APIError(message: "Other error", code: "other", args: [error.localizedDescription])))
}
}
}
}
这样,我就可以这样称呼我的 API:
AF.request(request).validate().responseTwoDecodable(of: [Item].self) { response in
switch response {
case .success(let items):
completion(.success(items))
case .failure(let error): //error is an APIError
log.error("Error while loading items: \(String(describing: error))")
completion(.failure(.couldNotLoad(underlyingError: error)))
}
}
我简单的认为200-299范围之外的任何状态码都对应一个错误。
我正在尝试将我的项目从 Alamofire 4.9 迁移到 5.3,但我很难处理错误。我想尽可能多地使用 Decodable
,但是当一切顺利时我的 API 端点 return 一个 JSON 结构,当一切顺利时另一个 JSON 结构有一个错误,所有端点上的所有错误都相同。我代码中对应的Codable
是ApiError
.
我想创建一个自定义响应序列化程序,它可以给我一个 Result<T, ApiError>
而不是默认的 Result<T, AFError>
。我发现 this article 似乎解释了一般过程,但其中的代码无法编译。
如何创建这样的自定义 ResponseSerializer
?
ResponseSerializer
s 有一个要求。在很大程度上,您可以只复制现有的序列化程序。例如,如果您想解析 CSV(不检查响应):
struct CommaDelimitedSerializer: ResponseSerializer {
func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> [String] {
// Call the existing StringResponseSerializer to get many behaviors automatically.
let string = try StringResponseSerializer().serialize(request: request,
response: response,
data: data,
error: error)
return Array(string.split(separator: ","))
}
}
您可以在 Alamofire's documentation 阅读更多内容。
我最终让它与以下 ResponseSerializer 一起工作:
struct APIError: Error, Decodable {
let message: String
let code: String
let args: [String]
}
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<APIError>(decoder: decoder)
public func serialize(request: URLRequest?, response: HTTPURLResponse?, data: Data?, error: Error?) throws -> Result<T, APIError> {
guard error == nil else { return .failure(APIError(message: "Unknown error", code: "unknown", args: [])) }
guard let response = response else { return .failure(APIError(message: "Empty response", code: "empty_response", args: [])) }
do {
if response.statusCode < 200 || response.statusCode >= 300 {
let result = try errorSerializer.serialize(request: request, response: response, data: data, error: 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(APIError(message: "Could not serialize body", code: "unserializable_body", 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, APIError>) -> 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(APIError(message: "Other error", code: "other", args: [error.localizedDescription])))
}
}
}
}
这样,我就可以这样称呼我的 API:
AF.request(request).validate().responseTwoDecodable(of: [Item].self) { response in
switch response {
case .success(let items):
completion(.success(items))
case .failure(let error): //error is an APIError
log.error("Error while loading items: \(String(describing: error))")
completion(.failure(.couldNotLoad(underlyingError: error)))
}
}
我简单的认为200-299范围之外的任何状态码都对应一个错误。