如何在Swift应用中反序列化AWS API Gateway+Lambda的响应?
AWS Lambda + API Gateway 集成后Swift请求JSON解码失败问题
问题详情
Lambda函数代码
def lambda_handler(event, context): # Logic return json.dumps({"name": "foo", "job": "bar"})
curl请求结果
通过API Gateway端点请求时,返回带转义的字符串形式JSON:
$ curl https://my-aws-gateway-endpoint/stage/endpoint "{\"name\": \"foo\", \"job\": \"bar\"}"
Swift请求错误
Swift应用发起请求时抛出解码错误:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found a string/data instead.", underlyingError: nil))
Swift请求代码
let semaphore = DispatchSemaphore(value: 0) var data: Data // This is a struct that contains the fields "name" and "job" let task = URLSession.shared.dataTask(with: url) { data, response, error in defer { semaphore.signal() } if let _ = error { return } guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { print("Invalid response: \(response?.description ?? \"")") return } guard let data = data else { print("No data received") return } do { let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) let jsonData = try JSONSerialization.data(withJSONObject: json, options: .fragmentsAllowed) let decoder = JSONDecoder() songData = try decoder.decode(Data.self, from: jsonData) } catch { print(error) return } } task.resume() semaphore.wait() return data
错误原因
- Lambda返回格式错误:手动用
json.dumps()将字典转为字符串后,API Gateway会把该字符串当作普通数据包装成JSON响应,最终客户端拿到的是包含JSON字符串的JSON值,而非直接的JSON对象。 - Swift解码逻辑错误:代码先将响应数据解析为JSON对象(实际是字符串),再转回Data后用
JSONDecoder解码结构体。此时解码器拿到的是字符串数据,而非预期的字典结构,触发类型不匹配错误。此外,结构体命名Data与Swift系统Data类重名,会导致语法混淆。
解决方案
方案1:修正Lambda代码(推荐,规范做法)
Lambda无需手动序列化JSON,直接返回Python字典即可。API Gateway会自动将其序列化为正确的JSON对象,并设置Content-Type: application/json响应头:
def lambda_handler(event, context): # Logic return {"name": "foo", "job": "bar"}
方案2:修正Swift代码(临时兼容方案)
若无法修改Lambda,可在Swift端先解析外层字符串,再转为Data后解码,同时修改结构体名称避免冲突:
// 重命名结构体,避免与系统Data类冲突 struct UserData: Codable { let name: String let job: String } let semaphore = DispatchSemaphore(value: 0) var songData: UserData? let task = URLSession.shared.dataTask(with: url) { data, response, error in defer { semaphore.signal() } if let error = error { print(error) return } guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { print("Invalid response: \(response?.description ?? "")") return } guard let data = data else { print("No data received") return } do { // 解析外层的JSON字符串 guard let jsonString = try JSONSerialization.jsonObject(with: data) as? String else { print("Failed to extract JSON string") return } // 将字符串转为Data guard let innerData = jsonString.data(using: .utf8) else { print("Failed to convert string to data") return } // 解码为结构体 let decoder = JSONDecoder() songData = try decoder.decode(UserData.self, from: innerData) } catch { print(error) return } } task.resume() semaphore.wait() return songData
内容的提问来源于stack exchange,提问作者dopatraman
相关产品推荐
相关产品推荐

