Swift 错误类型服务器响应来自 REST API 的错误输入

Posted

技术标签:

【中文标题】Swift 错误类型服务器响应来自 REST API 的错误输入【英文标题】:Swift error type server response for wrong input from rest API 【发布时间】:2020-02-10 18:28:43 【问题描述】:

希望你一切安好。我有个问题。我有一个带有电子邮件和密码的简单登录页面,还有一个类似的用户对象

// MARK: - UserModel
struct UserModel: Codable 
    let error: Bool
    let desc: String
    let user: User
    let token: String


// MARK: - User
struct User: Codable 
    let id: Int
    let email, firstName, lastName, lang: String
    let status: Int
    let referer, star: String?
    let phone: String?
    let ip: String?
    let birth, idNumber: String?
    let regionID: String?
    let createdAt, updatedAt: String

    enum CodingKeys: String, CodingKey 
        case id, email
        case firstName = "first_name"
        case lastName = "last_name"
        case lang, status, referer, star, phone, ip, birth
        case idNumber = "id_number"
        case regionID = "region_id"
        case createdAt, updatedAt
    

返回类型是上一个(UserModel)。如果用户输入了他/她的凭据,则没有问题。但是,如果他/她输入了错误的凭据,麻烦就开始了。我无法解析来自服务器的返回值。总是给我那一行的错误。

控制台输出为:

Rentover[2343:150674] Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Bool, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "error", intValue: nil)], debugDescription: "Expected to decode Bool but found a dictionary instead.", underlyingError: nil)): file

这是我的登录请求功能。为了简单起见,我使用了 codable。

class func requestLogIn(router: Router, completion: @escaping (Result<UserModel, Error>) -> ()) 

    guard let url = setUrlComponents(router: router).url else  return 
    var urlRequest = URLRequest(url: url)
    urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
    urlRequest.httpMethod = router.method

    if router.method == "POST"
        let model = LoginModel(email: router.parameters[0], password: router.parameters[1])
        urlRequest.httpBody = try? JSONEncoder().encode(model)
    

    let dataTask = URLSession.shared.dataTask(with: urlRequest)  data, response, error in

        guard error == nil else 
            print(error?.localizedDescription)
            return
        
        guard response != nil else 
            print("no response")
            return
        
        guard let data = data else 
            print("no data")
            return
        

        let responseObject = try! JSONDecoder().decode(UserModel.self, from: data)
        print(responseObject.user)
        DispatchQueue.main.async 
            completion(.success(responseObject))
        


    
    dataTask.resume()

这是我的错误结构。

struct LogInError: Codable, Error
    let error: Bool
    let desc: String
    let fields: [String] ----> 'Edit here old: let fileds: [String' 

最后我真正的调用函数是这样的

    NetworkService.requestLogIn(router: Router.login(email: nameTextField.text!, passowrd: passwordTextField.text!))  (result) in
        switch result 
        case .success(let userModel):
            print("RESULT SUCCESS")
            print("Hello \(userModel.user.firstName)")

            let selectedVC = UIUtils.checkUserStatus(status: userModel.user.status)
            self.navigationController?.modalPresentationStyle = .fullScreen
            self.navigationController?.pushViewController(selectedVC, animated: true)
        case .failure(let error):
            print("RESULT FAILED")
            print(error)
        
    

我按照that medium 链接创建了我的路由器和网络服务。如果您能帮助我解决这个问题,我将非常高兴和感激。或者给我一些关于网络 API 和使用的建议。

[编辑来自服务器的错误响应] 我的请求和响应消息体框架也是这样的:

祝你有美好的一天。和良好的编码。

【问题讨论】:

该错误表示如果发生错误,键 error 的值是一个字典。因此,LogInError 似乎与 JSON 结构不匹配。请添加 JSON 字符串。并且永远不要在Decodable 上下文中使用try!。始终catch 并处理错误。 你好@vadian,不幸的是我的JSON结构是正确的。我正在使用邮递员。我的问题是,当响应是来自我的休息服务的错误时。如何检测和解码针对特定错误的错误响应。 如我所说,添加错误返回的 JSON。 @vadian 我编辑了这个问题。你能再看一遍吗? 归档!= 字段。尝试!意思是“如果发生错误,程序崩溃并销毁任何错误的证据”。 try 表示“如果发生错误,返回可以打印的错误”。 【参考方案1】:

要解码两个不同的 JSON 字符串,一个方便的解决方案是使用关联类型的枚举,因为它可以非常具有描述性地表示 successfailure 情况。

首先它解码常见的error 密钥,然后解码UserModelLogInError

enum Response : Decodable 
    case success(UserModel), failure(LogInError)

    private enum CodingKeys : String, CodingKey  case error 

    init(from decoder: Decoder) throws 
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let hasError = try container.decode(Bool.self, forKey: .error)
        if hasError 
            let errorContainer = try decoder.singleValueContainer()
            let errorData = try errorContainer.decode(LogInError.self)
            self = .failure(errorData)
         else 
            let successContainer = try decoder.singleValueContainer()
            let successData = try successContainer.decode(UserModel.self)
            self = .success(successData)
        
    

使用它

class func requestLogIn(router: Router, completion: @escaping (Result<Response, Error>) -> ()) 

    ...

    do 
        let responseObject = try JSONDecoder().decode(Response.self, from: data)
        print(responseObject)
        DispatchQueue.main.async 
            completion(.success(responseObject))
        
     catch 
        DispatchQueue.main.async 
            completion(.failure(error))
        
    

NetworkService.requestLogIn(router: Router.login(email: nameTextField.text!, passowrd: passwordTextField.text!))  (response) in
    switch response 
    case .success(let result):
        switch result 
            case .success(let userModel): 
                print("RESULT SUCCESS")
                print("Hello \(userModel.user.firstName)")

                let selectedVC = UIUtils.checkUserStatus(status: userModel.user.status)
                self.navigationController?.modalPresentationStyle = .fullScreen
                self.navigationController?.pushViewController(selectedVC, animated: true)
            case .failure(let errorData): 
                print(errorData)
        

    case .failure(let error):
        print("RESULT FAILED")
        print(error)
    

LoginError 声明为标准的可解码结构

struct LogInError: Decodable 

【讨论】:

我实现了你的答案并且它有效。也非常合乎逻辑,非常感谢您的时间。只是您回答中的最后一个问题,您将 LogInError:Codable 更改为 LogInError:Decodable。原因是该结构不会与任何发布请求(任何编码过程)相关? LoginErrorLoginModel 不一样。 是的,所以对于LoginError不需要保持从Decodable继承吗? LoginError 必须符合 Decodable 不像 LoginModel 非常感谢先生!祝你有美好的一天。

以上是关于Swift 错误类型服务器响应来自 REST API 的错误输入的主要内容,如果未能解决你的问题,请参考以下文章