如何解码 Combine 中的错误响应消息?
How to decode error response message in Combine?
我正在使用 SwiftUI 和 Combine 进行登录。您能否告诉我一些想法,当用户输入错误的电子邮件或密码时,我该如何解码并显示 json 错误?我只能得到token
当我使用不正确的电子邮件或密码执行相同的登录请求时,服务器 returns 我收到此错误消息:
{
"code": "[jwt_auth] incorrect_password",
"message": "Incorrect password!",
"data": {
"status": 403
}
}
问题是我无法理解在 Combine 中执行一个请求时如何解码两个不同的 json 响应?我只能得到token
这是登录请求的模型:
struct LoginResponse: Decodable {
let token: String }
struct ErrorResponse: Decodable {
let message: String
}
struct Login: Codable {
let username: String
let password: String
}
static func login(email: String, password: String) -> AnyPublisher<LoginResponse, Error> {
let url = MarketplaceAPI.jwtAuth!
var request = URLRequest(url: url)
let encoder = JSONEncoder()
let login = Login(username: email, password: password)
let jsonData = try? encoder.encode(login)
request.httpBody = jsonData
request.httpMethod = HTTPMethod.POST.rawValue
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
return URLSession.shared
.dataTaskPublisher(for: request)
.print()
.receive(on: DispatchQueue.main)
.map(\.data)
.decode(
type: LoginResponse.self,
decoder: JSONDecoder())
.eraseToAnyPublisher()
}
并且在视图模型中:
MarketplaceAPI.login(email: email, password: password)
.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
print("finished")
case .failure(let error):
print("Failure error:", error.localizedDescription) // This's returning token error
}
},
receiveValue: { value in
print("Token:", value.token)
}
})
.store(in: &subscriptions)
}
您可以结合使用 tryMap
和 combine 来找出函数应该 return 的位置。我建议您阅读有关它的文档,但这里有一个片段应该可以让您继续使用它。
希望这就是您提出问题的意思 - 我已经更改了一些内容,但您可以随意将代码作为构建块并根据需要进行调整!
enum LoginError: Error, Equatable {
case noConnection
case invalidResponse
}
static func login(email: String, password: String) -> AnyPublisher<Void, LoginError> {
return URLSession.shared
.dataTaskPublisher(for: request)
.print()
.receive(on: DispatchQueue.main)
.mapError { _ in LoginError.noConnection }
.tryMap { (data, response) in
guard let response = response as? HTTPURLResponse else {
throw LoginError.invalidResponse
}
if response.statusCode == 200 {
return data
} else {
throw LoginError.invalidResponse
}
}
.decode(type: LoginResponse.self, decoder: JSONDecoder())
.tryMap { [unowned self] in
// Update your session with the token here using [=10=] then return.
// e.g session.token = [=10=].token
return
}
.mapError { [=10=] as? LoginError ?? .invalidResponse }
.eraseToAnyPublisher()
}
我会让 ErrorResponse
符合 Error
协议:
struct ErrorResponse: Decodable, Error {
let message: String
}
然后,使用 tryMap
而不是 decode
(这是 tryMap
的一种特殊情况)。
.tryMap({ data -> LoginResponse in
let decoder = JSONDecoder()
guard let loginResponse = try? decoder.decode(LoginResponse.self, from: data) else {
throw try decoder.decode(ErrorResponse.self, from: data)
}
return loginResponse
})
首先,尝试将数据解码为 LoginResponse
。注意这里使用 try?
。这样我们就可以检查它是否失败了。如果失败,我们将抛出一个错误。我们抛出的错误是解码为 ErrorResponse
的数据,或者在解码期间抛出的任何错误。
在您的视图模型中,您可以这样检查错误:
.sink { completion in
switch completion {
case .failure(let error as ErrorResponse):
// wrong password/username
// you can access error.message here
case .failure(let error):
// some other sort of error:
default:
break
}
} receiveValue: { loginResponse in
...
}
我正在使用 SwiftUI 和 Combine 进行登录。您能否告诉我一些想法,当用户输入错误的电子邮件或密码时,我该如何解码并显示 json 错误?我只能得到token
当我使用不正确的电子邮件或密码执行相同的登录请求时,服务器 returns 我收到此错误消息:
{
"code": "[jwt_auth] incorrect_password",
"message": "Incorrect password!",
"data": {
"status": 403
}
}
问题是我无法理解在 Combine 中执行一个请求时如何解码两个不同的 json 响应?我只能得到token
这是登录请求的模型:
struct LoginResponse: Decodable {
let token: String }
struct ErrorResponse: Decodable {
let message: String
}
struct Login: Codable {
let username: String
let password: String
}
static func login(email: String, password: String) -> AnyPublisher<LoginResponse, Error> {
let url = MarketplaceAPI.jwtAuth!
var request = URLRequest(url: url)
let encoder = JSONEncoder()
let login = Login(username: email, password: password)
let jsonData = try? encoder.encode(login)
request.httpBody = jsonData
request.httpMethod = HTTPMethod.POST.rawValue
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
return URLSession.shared
.dataTaskPublisher(for: request)
.print()
.receive(on: DispatchQueue.main)
.map(\.data)
.decode(
type: LoginResponse.self,
decoder: JSONDecoder())
.eraseToAnyPublisher()
}
并且在视图模型中:
MarketplaceAPI.login(email: email, password: password)
.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
print("finished")
case .failure(let error):
print("Failure error:", error.localizedDescription) // This's returning token error
}
},
receiveValue: { value in
print("Token:", value.token)
}
})
.store(in: &subscriptions)
}
您可以结合使用 tryMap
和 combine 来找出函数应该 return 的位置。我建议您阅读有关它的文档,但这里有一个片段应该可以让您继续使用它。
希望这就是您提出问题的意思 - 我已经更改了一些内容,但您可以随意将代码作为构建块并根据需要进行调整!
enum LoginError: Error, Equatable {
case noConnection
case invalidResponse
}
static func login(email: String, password: String) -> AnyPublisher<Void, LoginError> {
return URLSession.shared
.dataTaskPublisher(for: request)
.print()
.receive(on: DispatchQueue.main)
.mapError { _ in LoginError.noConnection }
.tryMap { (data, response) in
guard let response = response as? HTTPURLResponse else {
throw LoginError.invalidResponse
}
if response.statusCode == 200 {
return data
} else {
throw LoginError.invalidResponse
}
}
.decode(type: LoginResponse.self, decoder: JSONDecoder())
.tryMap { [unowned self] in
// Update your session with the token here using [=10=] then return.
// e.g session.token = [=10=].token
return
}
.mapError { [=10=] as? LoginError ?? .invalidResponse }
.eraseToAnyPublisher()
}
我会让 ErrorResponse
符合 Error
协议:
struct ErrorResponse: Decodable, Error {
let message: String
}
然后,使用 tryMap
而不是 decode
(这是 tryMap
的一种特殊情况)。
.tryMap({ data -> LoginResponse in
let decoder = JSONDecoder()
guard let loginResponse = try? decoder.decode(LoginResponse.self, from: data) else {
throw try decoder.decode(ErrorResponse.self, from: data)
}
return loginResponse
})
首先,尝试将数据解码为 LoginResponse
。注意这里使用 try?
。这样我们就可以检查它是否失败了。如果失败,我们将抛出一个错误。我们抛出的错误是解码为 ErrorResponse
的数据,或者在解码期间抛出的任何错误。
在您的视图模型中,您可以这样检查错误:
.sink { completion in
switch completion {
case .failure(let error as ErrorResponse):
// wrong password/username
// you can access error.message here
case .failure(let error):
// some other sort of error:
default:
break
}
} receiveValue: { loginResponse in
...
}