REST-API 为状态码 200 返回纯文本,为状态码 400 返回 json

问题描述

API 返回文本(字符串)http 状态代码 200

时,我需要知道什么是最佳实践或最佳方法

但它也为 http 状态码 400

返回 json 对象

我有使用原生 URLSession 构建的网络层,并使用 JSONDecoder 来解析 JSON

因此,在调用函数时,它采用通用参数,例如[Product](产品数组)对象,它将为我们提供产品数组

我的问题是,API 的结构或制作是经过良好实践的,以及使用 swift ios 解析 json 的最佳实践是什么

已编辑 - 附加信息

假设您有 api 端点 base_url/api/v1/get-otp-code 并且您正在发布您的电话号码:

Method: POST 
url: base_url/api/v1/get-otp-code 
params: { "phone": "123456789" } 

如果您之前确实请求过 OTP,则此端点将返回 json 值 回复{"error":"some error","message": "some message"}

但是如果您是第一次请求,它将为您提供字符串值 回复"dwaotpwaadd-dadwaawwdcwdadodwde"

所以如果你不知道什么类型会返回,你应该让它动态

解决方法

嗨@nikakirkitadze,欢迎来到我们的社区!

正如您所知,每个请求都可能带有错误,因此您也必须在通用方法中处理这些错误。

假设您有以下通用方法来执行获取请求:

func getRequest<T: Codable,U: Codable>(completion: @escaping(_ response: T?,_ error: HTTPClientError<U>?) -> Void) {
        URLSession.shared.dataTask(with: URLRequest(url: URL(string: "")!)) { (data,response,error) in
            guard let statusCode = (response as? HTTPURLResponse)?.statusCode,let data = data else {
                completion(nil,HTTPClientError(type: .invalidResponse,model: nil))
                return
            }
            
            if statusCode == 400 {
                let decodedErrorData = try? JSONDecoder().decode(U.self,from: data)
                completion(nil,HTTPClientError(statusCode: statusCode,type: .AUTH_FAILED,model: decodedErrorData))
                return
            }
            
            // Success
            do {
                let decodedData = try JSONDecoder().decode(T.self,from: data)
                completion(decodedData,nil)
            } catch {
                print(error)
                let decodedErrorData = try? JSONDecoder().decode(U.self,type: .parsingError,model: decodedErrorData))
            }
            
        }.resume()
    }

ExampleResponse 模型和通用 HTTPClientError:

struct ExampleResponse: Codable { 
   // Add your coding keys here..
}

public final class HTTPClientError<T: Codable>: Error {
    
    public let statusCode: Int?
    public let type: Code
    public let model: T?
    
    public enum Code: Int {
        case none
        case invalidResponse
        case invalidRequest
        case parsingError
        case AUTH_FAILED = 401
        case FAILED = 500
        case SERVICE_UNAVAILABLE = 501
    }
    
    public required init(statusCode: Int? = nil,type: Code,model: T? = nil) {
        self.statusCode = statusCode
        self.type = type
        self.model = model
    }
}

如您所见,我们使用通用类型作为编码类型创建了一个通用错误。

现在你可以像这样调用你的方法:

func getExampleResponse(completion: @escaping(_ response: ExampleResponse?,_ error: HTTPClientError<String>?) -> Void) {
     getRequest(completion: completion)
}

因此,关于您的请求和您正在等待的错误,您可以调整泛型类型以满足您的需求。

这里的最佳做法是:

  • 使用 Swift Codables 来映射您的回复
  • 始终检查是否有错误
  • 创建通用方法以避免重复代码

您可以查看我关于网络的轻量级 swift 包以获取更多帮助:https://github.com/kstefanou52/SKHTTPClient


更新未知响应类型

如果您不知道响应的类型,则必须尝试转换响应,如下所示:

func getRequest(completion: @escaping(_ response: String?,_ error: [String: String]?) -> Void) {
        URLSession.shared.dataTask(with: URLRequest(url: URL(string: "")!)) { (data,["error": "empty response"])
                return
            }
            
            if let stringResponse = String(data: data,encoding: .utf8) {
                completion(stringResponse,nil)
            } else if let dictionaryResponse = try? JSONDecoder().decode([String: String].self,from: data) {
                completion(nil,dictionaryResponse)
            } else {
                completion(nil,["error": "unknown response"])
            }
            
        }.resume()
    }