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

Swift中使用Alamofire调用POST API无法获取JSON响应问题

解决NetworkManager中POST请求参数无效的问题

问题概述

我在NetworkManager中实现了通用请求方法serviceCall,但该方法仅对GET请求有效,使用POST请求并携带参数时返回解析错误。

错误信息

{
  "code": "-32700",
  "meaning": "Undefined array key \"params\"",
  "message": "Parse error"
}

测试代码

func testServiceCall(){
    let parameters = ["name": "testinggg", "job": "manager"] as [String : Any]
    let request = RequestObject(params: parameters, method: .post, path: "https://reqres.in/api/users", isTokenNeed: false, vc: self)
    WebService.sharedInstance.serviceCall(UserModel.self, with: request) { (response, error) in
        print("user post response \(response)")
    }
}

NetworkManager代码

class GeneralResponse<T: Codable>: Codable {
    var jsonrpc: String
    var result: T?
    let error: ErrorClass?
}

struct ErrorClass: Codable {
    let code, message, meaning: String?
}

struct RequestObject {
    var params: [String: Any]?
    var method: HTTPMethod
    var path: String
    var isTokenNeed: Bool = false
    var vc: UIViewController?
}

class NetworkManager {
    private let decoder: JSONDecoder
    static let sharedInstance = NetworkManager()
    
    public init(_ decoder: JSONDecoder = JSONDecoder()) {
        self.decoder = decoder
    }
    
    public func serviceCall<T: Codable>(_ objectType: T.Type,
                                        with request: RequestObject,
                                        completion: @escaping  (T?, Error?) -> Void)  {
        
        AF.request(request.path, method: request.method, parameters: request.params,encoding: JSONEncoding.default, headers: request.isTokenNeed ? ["Authorization" : "Bearer hnjjdshjdsh", "Accept": "application/json"] : ["": ""])
            .responseJSON { response in
                
                switch response.result {
                case .success(_):
                    do {
                        let data = response.data
                        let responseData  = try self.decoder.decode(T.self, from: data ?? Data())
                        completion(responseData, nil)
                    } catch {
                        completion(nil, error)
                        print("in catch \(error.localizedDescription)")
                    }
                case .failure(let AFError):
                    let error = AFError
                    print(error.localizedDescription)
                    print("failure error: \(error)")
                }
            }
    }
}

问题分析

  1. 参数格式不匹配:后端报错提示找不到params键,说明它期望请求体是一个包含params字段的JSON对象,但当前代码直接把参数作为根节点发送,不符合后端的jsonrpc格式要求。
  2. 无效请求头:当不需要Token时,代码传入了["": ""]作为请求头,这会生成一个无效的空键头,可能导致服务器解析异常。
  3. 响应解析错误:后端返回的是GeneralResponse<T>格式的jsonrpc响应,但代码直接解析T类型,导致解析失败。

解决方案

修改后的NetworkManager核心代码

class NetworkManager {
    private let decoder: JSONDecoder
    static let sharedInstance = NetworkManager()
    
    public init(_ decoder: JSONDecoder = JSONDecoder()) {
        self.decoder = decoder
    }
    
    public func serviceCall<T: Codable>(_ objectType: T.Type,
                                        with request: RequestObject,
                                        completion: @escaping  (T?, ErrorClass?) -> Void)  {
        
        // 将参数包装到params字段中,符合后端jsonrpc格式要求
        let parameters = request.params.map { ["params": $0] }
        
        // 修复请求头:不需要Token时传nil,避免无效空键
        let headers: HTTPHeaders? = request.isTokenNeed ? [
            "Authorization": "Bearer hnjjdshjdsh",
            "Accept": "application/json"
        ] : nil
        
        AF.request(request.path,
                   method: request.method,
                   parameters: parameters,
                   encoding: JSONEncoding.default,
                   headers: headers)
            .responseJSON { response in
                
                switch response.result {
                case .success(_):
                    guard let data = response.data else {
                        completion(nil, ErrorClass(code: "1", message: "无响应数据", meaning: "Empty response data"))
                        return
                    }
                    do {
                        // 先解析外层的GeneralResponse,再取出result数据
                        let generalResponse = try self.decoder.decode(GeneralResponse<T>.self, from: data)
                        completion(generalResponse.result, generalResponse.error)
                    } catch {
                        completion(nil, ErrorClass(code: "\(error._code)", message: error.localizedDescription, meaning: "解析失败"))
                        print("解析错误: \(error.localizedDescription)")
                    }
                case .failure(let afError):
                    completion(nil, ErrorClass(code: "\(afError._code)", message: afError.localizedDescription, meaning: "网络请求失败"))
                    print("请求失败: \(afError)")
                }
            }
    }
}

调整测试代码的回调处理

func testServiceCall(){
    let parameters = ["name": "testinggg", "job": "manager"] as [String : Any]
    let request = RequestObject(params: parameters, method: .post, path: "https://reqres.in/api/users", isTokenNeed: false, vc: self)
    WebService.sharedInstance.serviceCall(UserModel.self, with: request) { (response, error) in
        if let error = error {
            print("请求错误: \(error.message ?? "未知错误")")
        } else {
            print("user post response \(response)")
        }
    }
}

关键修改点说明

  • 参数包装:将请求参数包裹在params字段中,匹配后端对jsonrpc格式的要求,解决"Undefined array key 'params'"错误。
  • 请求头修复:移除无效的空键请求头,改为nil,避免服务器解析异常。
  • 响应解析调整:先解析外层的GeneralResponse<T>,再提取内部的result数据,确保和后端返回的响应格式一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:01:25