You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Xcode的URLSession接收NestJS API的任意响应?

问题:Xcode中如何接收NestJS API的任意响应(无需硬编码结构)

我有一个向NestJS API发送HTTP POST请求的Swift文件,网站端能正常接收API返回的任意响应,但在Xcode里必须硬编码响应结构。我原本以为Xcode只支持JSON响应,现在想确认能不能让Xcode接收API返回的任意响应,并寻求解决方案。

HttpPOST.swift

import Foundation
func apiCall(url: String, body: Dictionary<String, AnyHashable>, completion: @escaping (Response) -> Void){
    
    guard let url = URL(string: "http://192.168.1.71:3000/user/login")else{
        return
    }
    
    var request = URLRequest(url: url)
    // method, body, headers
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    let body: [String: AnyHashable] = body
    request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
    
    // make rqs
    let task = URLSession.shared.dataTask(with: request){ data, response, error in
        guard let data = data,error == nil else {
            return
        }
        
        do {
            let response = try JSONDecoder().decode(Response.self, from: data)
            completion(response)
        }
        
        catch {
            print(error)
        }
        
    }
    
    task.resume()
}

struct Response: Codable {
    var usernameError: String?;
    var passwordError: String?;
    var logOk: Bool?;
    var token: String?;
}

响应示例

Response(usernameError: Optional("enteruname"), passwordError: Optional("enterpasswd"), logOk: Optional(false), token: nil)

解决方案

首先明确:Xcode(准确说是Swift的URLSession)完全支持接收任意格式的响应,不止JSON。你之前的代码只是强制把响应解码成了固定的Response结构,所以才只能处理符合该结构的JSON。以下是几种不同场景的解决方案:

方案1:接收任意JSON响应(无需硬编码结构)

如果API返回的还是JSON,但结构不固定,可以把响应解码成[String: Any]或者Any类型,摆脱固定结构的限制:

修改后的apiCall函数:

import Foundation

func apiCall(url: String, body: [String: AnyHashable], completion: @escaping (Any?, Error?) -> Void) {
    guard let url = URL(string: "http://192.168.1.71:3000/user/login") else {
        completion(nil, NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效的URL"]))
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else {
            completion(nil, error)
            return
        }
        
        do {
            let jsonResponse = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
            completion(jsonResponse, nil)
        } catch {
            completion(nil, error)
        }
    }
    
    task.resume()
}

使用示例:

apiCall(url: "", body: ["username": "test", "password": "123"]) { response, error in
    if let json = response as? [String: Any] {
        let usernameError = json["usernameError"] as? String
        let logOk = json["logOk"] as? Bool
        let token = json["token"] as? String
        // 根据实际返回字段按需处理
    }
}

方案2:接收任意格式的响应(非JSON)

如果API可能返回非JSON格式(比如纯文本、XML等),可以直接处理原始Data,或者转换成字符串:

修改后的apiCall函数:

import Foundation

func apiCall(url: String, body: [String: AnyHashable], completion: @escaping (Data?, Error?) -> Void) {
    guard let url = URL(string: "http://192.168.1.71:3000/user/login") else {
        completion(nil, NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效的URL"]))
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        completion(data, error)
    }
    
    task.resume()
}

转换成字符串处理的示例:

apiCall(url: "", body: ["username": "test", "password": "123"]) { data, error in
    if let data = data, let responseString = String(data: data, encoding: .utf8) {
        print("原始响应内容:\(responseString)")
        // 根据响应格式(比如XML、纯文本)自行解析
    }
}

方案3:保留类型安全同时支持灵活响应

如果既想保留类型安全,又要处理不同结构的响应,可以用枚举+自定义解码的方式,覆盖API可能返回的所有情况:

定义响应枚举:

enum APIResponse: Codable {
    case loginSuccess(token: String)
    case loginError(usernameError: String?, passwordError: String?)
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let logOk = try container.decodeIfPresent(Bool.self, forKey: .logOk) ?? false
        
        if logOk, let token = try container.decodeIfPresent(String.self, forKey: .token) {
            self = .loginSuccess(token: token)
        } else {
            let usernameError = try container.decodeIfPresent(String.self, forKey: .usernameError)
            let passwordError = try container.decodeIfPresent(String.self, forKey: .passwordError)
            self = .loginError(usernameError: usernameError, passwordError: passwordError)
        }
    }
    
    enum CodingKeys: String, CodingKey {
        case logOk, token, usernameError, passwordError
    }
    
    func encode(to encoder: Encoder) throws {
        // 若无需编码请求数据可忽略此方法
    }
}

修改apiCall的completion类型和解码逻辑:

func apiCall(url: String, body: [String: AnyHashable], completion: @escaping (APIResponse?, Error?) -> Void) {
    // 前面的请求构建逻辑不变
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else {
            completion(nil, error)
            return
        }
        
        do {
            let response = try JSONDecoder().decode(APIResponse.self, from: data)
            completion(response, nil)
        } catch {
            completion(nil, error)
        }
    }
    
    task.resume()
}

使用示例:

apiCall(url: "", body: ["username": "test", "password": "123"]) { response, error in
    guard let response = response else { return }
    
    switch response {
    case .loginSuccess(let token):
        print("登录成功,Token:\(token)")
    case .loginError(let usernameErr, let passwordErr):
        print("登录失败:用户名错误\(usernameErr ?? ""),密码错误\(passwordErr ?? "")")
    }
}

内容的提问来源于stack exchange,提问作者Alms

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 00:37:45