【问题标题】:How to decode error response message in Combine?如何在组合中解码错误响应消息?
【发布时间】:2022-01-02 19:50:04
【问题描述】:

我正在使用 SwiftUI 和 Combine 进行登录。您能否给我一些想法,当用户输入错误的电子邮件或密码时,如何解码并显示 json 错误?我只能拿到token。

当我使用错误的电子邮件或密码执行相同的登录请求时,服务器会返回此错误消息:

{
"code": "[jwt_auth] incorrect_password",
"message": "Incorrect password!",
"data": {
    "status": 403
}

}

问题是我无法理解在组合中执行一个请求时如何解码两个不同的 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()
}

在 viewModel 中:

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)
}

【问题讨论】:

  • 当您只显示一个 JSON 响应时,“两个不同的 json 响应”是什么意思?你的意思是登录成功的时候会回复{"token": "xxx"}这样的东西吗?
  • 实现 init(from decoder 并将 JSON 解码为带有 successfailure 案例的关联值的枚举。然后在枚举上的decode 运算符switch 之后,在failure 情况下抛出错误。
  • 是的,如果响应成功,服务器只返回token。如果用户在一个组合功能中输入错误的密码或电子邮件,我无法理解如何解码错误消息
  • @vadian 谢谢你的想法,我会试试的。

标签: swift combine


【解决方案1】:

我会让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
    ...
}

【讨论】:

    【解决方案2】:

    您可以使用 tryMap 和 combine 来确定函数应该返回的位置。我建议您阅读有关它的文档,但这里有一个 sn-p 应该能够让您使用它。

    希望这就是您所说的问题 - 我已经更改了一些内容,但您可以随意将代码作为构建块并根据需要进行调整!

    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 $0 then return. 
                // e.g session.token = $0.token
    
                return
            }
            .mapError { $0 as? LoginError ?? .invalidResponse }
            .eraseToAnyPublisher()
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-11
      • 2020-02-22
      • 2016-04-16
      • 1970-01-01
      相关资源
      最近更新 更多