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
相关产品推荐
相关产品推荐

