【发布时间】:2022-01-21 03:42:27
【问题描述】:
是否可以解码来自服务器的错误响应,该响应在对象内部具有未知键?我将如何处理这样的回应?
现在我像这样在 URLSession 上做了一个扩展
extension URLSession {
func post<T: Decodable, U: Encodable>(
_ type: T.Type = T.self,
data: U,
from url: URL,
keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .convertFromSnakeCase
) async throws -> T {
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let body = try JSONEncoder().encode(data)
let token = UserDefaults.standard.string(forKey: "AccessToken")
if token != nil {
request.setValue("Bearer \(token!)", forHTTPHeaderField: "Authorization")
}
do {
let (data, response) = try await upload(for: request, from: body)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = keyDecodingStrategy
let decoded = try decoder.decode(T.self, from: data)
return decoded
} catch {
print(error)
throw error
}
}
}
这样我可以向后端发出 POST 请求并取回解码的对象 所以当我尝试登录时,我可以这样做:
func login(email: String, password: String) async {
let body = LoginRequest(email: email, password: password)
let url = URL(string: "https://api.junoreader.com/api/auth/login")!
do {
let response = try await URLSession.shared.post(DetailModel<UserModel>.self, data: body, from: url)
setUser(data: response.data)
} catch {
print("api error")
print(error)
}
}
响应的样子
struct DetailModel<T: Codable>: Codable {
var data: T
}
struct UserModel: Codable, Identifiable, ObservableObject {
let id: Int
let firstName: String
let lastName: String
let username: String
let bio: String
var name: String {
return "\(firstName ?? "") \(lastName ?? "")"
}
}
但是,当登录凭据错误时,服务器会以 403 响应,并带有类似这样的 JSON 对象
{
"data": {
"errors": {
"login": [
"Email/password do not match."
]
}
}
}
数据和错误键始终存在,但根据我所做的请求和后端验证,“登录”可能会有所不同。所以“错误”也可以有多个键。
解码此类错误对象的最佳方法是什么?如何在“post”函数中处理这些错误?同样当错误发生而不是立即抛出错误时,swift仍在尝试解码数据。
【问题讨论】:
-
您可以创建一个可编码模型,其中
errors是[String: [String]],或者使用自定义初始化,使用枚举关联值来处理login键和其他(如果你知道什么更好可以是所有不同的键)... -
FWIW,你没有配置你的
JSONEncoder。你可能也想做日期编码/解码策略之类的事情。就个人而言,我建议将其移动到 API 服务对象中,而不是URLSession扩展中,避免重复重新配置这些帮助对象。此外,您的身份验证方案对于您的 API 是唯一的,而不是URLSession。我理解URLSession扩展的吸引力,但它确实属于API层,而不是网络层。
标签: swift concurrency urlsession