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

Swift使用Alamofire发送含字典数组参数时JSON编码异常问题

Alamofire POST请求嵌套数组参数编码异常解决方案

问题背景

开发中构造比赛信息请求参数时,points字段为得分点字典组成的数组,参数构造代码如下:

var allPoints : [[String: String]] = []
for playerScoreTracker in playerScoreTrackers!{
    for point in playerScoreTracker.points{
        let tempDict : [String : String] = ["type": point.typeOfPoint.rawValue, "scorer" : point.scorer.uuid]
        allPoints.append(tempDict)
    }
}
    
var parameters : Dictionary<String, Any> = [
    "playerOne": playerOne.uuid,
    ...
    "team_two_score": scoreboard.teamTwoScore,
    "confirmed": false,
    "points": allPoints
]

封装的Alamofire POST请求代码如下:

static func post<T: Decodable>(url: String, parameters: [String: Any], returnType: T.Type, completion: @escaping (Bool, Any) -> Void) {
    AF.request(url, method: .post, parameters: parameters, headers: getHeaders()).responseDecodable(of: returnType.self) { response in
        guard let returnStatusCode = response.response?.statusCode else {
            let returnData : [String] = ["No connection."]
            completion(false, returnData)
            return
        }
        switch returnStatusCode {
        case 200, 201:
            let returnData = response.value!
            completion(true, returnData)
        case 400:
            var errorsDict : [String: [String]] = [:]
            do {
                errorsDict = try JSONDecoder().decode(Dictionary<String, [String]>.self, from: response.data!)
            } catch {
                errorsDict["my_side_errors"] = ["Couldn't decode json response."]
            }
            let errors : [String] = getErrorsFromParams(params: parameters, errorsDict: errorsDict)
            completion(false, errors)
        default:
            let errors : [String] = ["Error."]
            completion(false, errors)
        }
    }
}

异常表现

发送请求后参数格式不符合预期,points字段的每个字典键被拆解为独立数组,实际传输的参数格式如下:

{
    "confirmed": ["0"], 
    "playerFour": ["8b52d186-ab60-438e-92f1-a3d35430ea81"], 
    "playerOne": ["f33e09b5-be8b-49e7-99c2-1e2df7512a71"], 
    "playerThree": ["583e0143-7352-4907-a806-a3b16baceefc"], 
    "playerTwo": ["24da0653-7fbc-41c3-8a47-bdefb9323eb2"], 
    "points[][type]": ["tk", "tk"], 
    "points[][scorer]": ["8b52d186-ab60-438e-92f1-a3d35430ea81", "8b52d186-ab60-438e-92f1-a3d35430ea81"], 
    "team_one_score": ["12"], 
    "team_two_score": ["0"], 
    "time_ended": ["2021-09-11 10:54:00"], 
    "time_started": ["2021-09-11 10:53:57"]
}

Postman发送标准JSON格式参数可正常对接后端,正确格式如下:

{
    "playerOne": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", 
    "playerTwo": "99028402-abdd-42dd-a8d9-48c05ba2787e", 
    "type": "pu", 
    "team_two_score": 0, 
    "time_started": "2021-09-11 12:33:44", 
    "time_ended": "2021-09-11 12:33:48", 
    "points": [
        {"scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "tk"}, 
        {"scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "tk"}, 
        {"type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"}, 
        {"type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"}, 
        {"type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"}, 
        {"scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "tk"}
    ],
    "playerThree": "24da0653-7fbc-41c3-8a47-bdefb9323eb2", 
    "playerFour": "583e0143-7352-4907-a806-a3b16baceefc", 
    "team_one_score": 12, 
    "confirmed": false
}

本地打印的请求参数结构是正确的:

params: [
    "team_one_score": 13, 
    "points": [
        ["type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"], 
        ["scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "tk"], 
        ["type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"], 
        ["type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"], 
        ["type": "tk", "scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"], 
        ["scorer": "f33e09b5-be8b-49e7-99c2-1e2df7512a71", "type": "sk"]
    ], 
    "time_started": "2021-09-11 12:12:26", 
    "confirmed": false, 
    "time_ended": "2021-09-11 12:12:30", 
    "playerThree": "583e0143-7352-4907-a806-a3b16baceefc",
    "team_two_score": 0, 
    "playerTwo": "24da0653-7fbc-41c3-8a47-bdefb9323eb2",
    "playerFour": "8b52d186-ab60-438e-92f1-a3d35430ea81",
    "playerOne": "f33e09b5-be8b-49e7-99c2-1e2df7512a71"
]

cURL日志显示请求的Content-Type为application/x-www-form-urlencoded,说明参数是表单格式编码:

$ curl -v \
    -X POST \
    -H "User-Agent: DayOfDie/1.0 (aberard.DayOfDie; build:1; iOS 14.5.0) Alamofire/5.4.1" \
    -H "Accept-Encoding: br;q=1.0, gzip;q=0.9, deflate;q=0.8" \
    -H "Accept-Language: en;q=1.0" \
    -H "Authorization: Token be1d6e220931d0249e3d600ef9682909f39252ec" \
    -H "Content-Type: application/x-www-form-urlencoded; charset=utf-8" \
    -d "confirmed=0&playerFour=8b52d186-ab60-438e-92f1-a3d35430ea81&playerOne=f33e09b5-be8b-49e7-99c2-1e2df7512a71&playerThree=583e0143-7352-4907-a806-a3b16baceefc&playerTwo=24da0653-7fbc-41c3-8a47-bdefb9323eb2&points%5B%5D%5Btype%5D=sk&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Btype%5D=tk&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Btype%5D=tk&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Btype%5D=tk&points%5B%5D%5Bscorer%5D=f33e09b5-be8b-49e7-99c2-1e2df7512a71&points%5B%5D%5Btype%5D=tk&team_one_score=11&team_two_score=0&time_ended=2021-09-11%2012%3A18%3A55&time_started=2021-09-11%2012%3A18%3A51" \
"http://127.0.0.1:8000/games/"

问题根因与修复方案

Alamofire的POST请求默认使用URL编码(对应表单格式),而非JSON编码,才会导致嵌套数组参数被拆解为表单键值对格式。
修复方法:在AF.request方法中显式指定编码方式为JSONEncoding.default即可,修改后的请求代码如下:

AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: getHeaders()).responseDecodable(of: returnType.self) { response in
    // 原有逻辑不变
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 08:15:03