英文:
Swift do/try/catch - accessing API response in catch block
问题
在Task
的catch
块中,您可以访问LoginResponse
中的"msg"如下:
} catch {
if let loginError = error as? LoginError {
let errorMessage = loginError.msg
// 处理错误消息
} else {
// 处理其他错误
}
}
为了实现这一点,您需要创建一个自定义错误类型LoginError
,该类型包含与LoginResponse
中的错误消息相对应的属性。然后,在发生错误时,您可以检查错误类型并访问相关的错误消息。
如果要重新组织do/catch
块以便在catch
块中访问LoginResponse
对象,您可以将LoginResponse
的声明移到do
块的范围内,以便在catch
块中也能访问它,如下所示:
Task {
do {
let request: Request<LoginResponse> = .postLogin(email: email, password: password)
let response = try await URLSession.shared.decode(request)
// store token if received
} catch {
if let loginError = error as? LoginError {
let errorMessage = loginError.msg
// 处理错误消息
} else {
// 处理其他错误
}
}
}
struct LoginResponse: Decodable {
let token: String
let msg: String
}
这样,您可以在catch
块中直接访问LoginResponse
对象,以及其中的"msg"属性。
英文:
Say I'm making an API call like this:
Task {
do {
let request: Request<LoginResponse> = .postLogin(email: email, password: password)
let response = try await URLSession.shared.decode(request)
// store token if received
} catch {
// ???
}
}
struct LoginResponse: Decodable {
let token: String
let msg: String
}
extension URLSession {
func decode<Value: Decodable>(
_ request: Request<Value>,
using decoder: JSONDecoder = .init()
) async throws -> Value {
let decoded = Task.detached(priority: .userInitiated) {
let (data, _) = try await self.data(for: request.urlRequest)
try Task.checkCancellation()
return try decoder.decode(Value.self, from: data)
}
return try await decoded.value
}
}
In the catch
block of the Task
, how would I access the "msg" from LoginResponse
? It's an error message being returned from the backend that the login info is incorrect. Or how would I restructure the do/catch so that I can access the LoginResponse
object in the catch block?
For further info on the Request
object, see here
答案1
得分: 1
如果您的后端在登录失败时仍然返回HTTP响应,那么try await self.data(for: request.urlRequest)
不会引发错误。它将返回一个HTTPURLResponse
(您完全忽略了它的_
部分),其中包含指示错误的statusCode
。
self.data(for: request.urlRequest)
仅在没有响应时引发异常,例如当您使用无效的URL或没有互联网连接时。
因此,您可以将HTTPURLResponse
返回给调用者,并在do
块中进行检查:
func decode<Value: Decodable>(
_ request: Request<Value>,
using decoder: JSONDecoder = .init()
) async throws -> (Value, HTTPUURLResponse?) {
let decoded = Task.detached(priority: .userInitiated) {
let (data, response) = try await self.data(for: request.urlRequest)
try Task.checkCancellation()
return try (
decoder.decode(Value.self, from: data),
response as? HTTPURLResponse // 如果请求是HTTP请求,此强制转换将成功
)
}
return try await decoded.value
}
let request: Request<LoginResponse> = .postLogin(email: email, password: password)
let (decodedData, response) = try await URLSession.shared.decode(request)
if let httpResponse = response, httpResponse.statusCode >= 400 {
// 处理带有decodedData的错误...
}
如果您希望在catch
块中处理它,您可以在任务中进行检查并引发错误。
func decode<Value: Decodable, Failure: Error & Decodable>(
_ request: Request<Value>,
using decoder: JSONDecoder = .init(),
errorResponseType: Failure.Type
) async throws -> Value {
let decoded = Task.detached(priority: .userInitiated) {
let (data, response) = try await self.data(for: request.urlRequest)
try Task.checkCancellation()
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode >= 400 {
throw try decoder.decode(errorResponseType, from: data)
}
return try decoder.decode(Value.self, from: data)
}
return try await decoded.value
}
// 这些可以是相同的类型,但我更喜欢将错误与成功响应分开
struct LoginResponse: Decodable {
let token: String
let msg: String
}
struct LoginError: Decodable, Error {
let token: String
let msg: String
}
do {
let request: Request<LoginResponse> = .postLogin(email: email, password: password)
let decodedData = try await URLSession.shared.decode(request, errorResponseType: LoginError.self)
} catch let error as LoginError {
// 处理登录错误...
} catch {
// 处理其他错误...
}
英文:
If your backend still gives you an HTTP response if the login failed, then try await self.data(for: request.urlRequest)
won't throw an error. It will return a HTTPURLResponse
(which you completely ignored with _
) with a statusCode
indicating an error.
self.data(for: request.urlRequest)
would only throw when there is no response at all, like when you used an invalid URL, or there is no internet connection.
Therefore, you can return the HTTPURLResponse
back to the caller, and check it in the do
block:
func decode<Value: Decodable>(
_ request: Request<Value>,
using decoder: JSONDecoder = .init()
) async throws -> (Value, HTTPUURLResponse?) {
let decoded = Task.detached(priority: .userInitiated) {
let (data, response) = try await self.data(for: request.urlRequest)
try Task.checkCancellation()
return try (
decoder.decode(Value.self, from: data),
response as? HTTPURLResponse // this cast will succeed if the request is an HTTP request
)
}
return try await decoded.value
}
let request: Request<LoginResponse> = .postLogin(email: email, password: password)
let (decodedData, response) = try await URLSession.shared.decode(request)
if let httpResponse = response, httpResponse.statusCode >= 400 {
// handle error with decodedData...
}
If you want to handle it in the catch
block instead, you can check this in the task, and throw an error instead.
func decode<Value: Decodable, Failure: Error & Decodable>(
_ request: Request<Value>,
using decoder: JSONDecoder = .init(),
errorResponseType: Failure.Type
) async throws -> Value {
let decoded = Task.detached(priority: .userInitiated) {
let (data, response) = try await self.data(for: request.urlRequest)
try Task.checkCancellation()
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode >= 400 {
throw try decoder.decode(errorResponseType, from: data)
}
return try decoder.decode(Value.self, from: data)
}
return try await decoded.value
}
// these could be the same type, but I prefer separating errors from successful responses
struct LoginResponse: Decodable {
let token: String
let msg: String
}
struct LoginError: Decodable, Error {
let token: String
let msg: String
}
do {
let request: Request<LoginResponse> = .postLogin(email: email, password: password)
let decodedData = try await URLSession.shared.decode(request, errorResponseType: LoginError.self)
} catch let error as LoginError {
// handle login error...
} catch {
// handle other errors...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论