Alamofire与Spring Boot交互时响应数据解码为nil问题排查
咱们直接说核心问题:客户端的模型结构和后端返回的JSON结构完全不匹配,而且后端的响应封装方式有错误,导致解码时找不到对应字段,最终得到的userData.body为nil,却没有抛出解析错误。
具体问题拆解
后端响应结构冗余且错误
你的后端RestResponse类里把data字段定义成了ResponseEntity<?>,这会导致Spring序列化时,把ResponseEntity的所有内部属性(比如headers、statusCode、body)都暴露出来。最终返回的JSON里,真正的User对象被嵌套在了data.body下面,而不是直接放在data字段里——你用Postman看到的响应已经很明显能看出来这个问题。客户端模型和后端结构不匹配
客户端的UserResponseData期望根节点有一个body字段来承载User对象,但后端返回的根节点根本没有body,而是把User藏在了data.body里。因为UserResponseData的body是可选类型(User?),JSONDecoder找不到对应字段时只会把它设为nil,不会抛出解析错误。
修复步骤
第一步:修复后端的响应封装
首先把RestResponse改成泛型类,并且不要用ResponseEntity作为data的类型,直接用普通业务对象:
public class RestResponse<T> { private String status = "failed"; private String errorMessage = "Error"; private T data = null; // 保留原有的getter和setter方法 }
然后修改控制器代码,直接返回RestResponse<User>,不需要嵌套ResponseEntity(Spring会自动帮你包装成正确的HTTP响应):
@RestController @RequestMapping("/signup") public class SignupController { @Autowired public UserDao userDao; @PostMapping() public RestResponse<User> signup(@RequestBody User newUser) { User user = userDao.addUser(newUser); user.setPassword("******"); RestResponse<User> response = new RestResponse<>(); response.setStatus("success"); response.setErrorMessage(null); response.setData(user); // 直接传入User对象,而非ResponseEntity return response; } }
修改后,后端返回的JSON会变成更简洁的结构:
{ "status": "success", "errorMessage": null, "data": { "name": "Elon", "email": "elon@s.c", "password": "******", "token": "Elon Token", "loginStatus": 0 } }
第二步:修改客户端模型匹配新结构
更新UserResponseData,让它对应后端返回的字段:
class UserResponseData: Codable { let status: String let errorMessage: String? let data: User? // 对应后端的data字段,里面直接是User对象 }
第三步:调整客户端解码逻辑
最后修改客户端的解码代码,从userResponse.data中获取User对象,同时避免强制解包response.data(不安全):
private func makeRequest(_ path: String, method: HTTPMethod, params: Parameters?, headers: HTTPHeaders?, completion: @escaping(User?) -> Void) { let enc = JSONEncoding.default let url = AppConstants.ENDPOINT + path Alamofire.request(url, method: .post, parameters: params, encoding: enc, headers: headers) .validate() .responseJSON { response in switch response.result { case .success: guard let responseData = response.data else { print("No response data received") completion(nil) return } do { let userResponse = try JSONDecoder().decode(UserResponseData.self, from: responseData) completion(userResponse.data) // 现在从data字段取User } catch let error { print("Failed to parse response: \(error.localizedDescription)") } case .failure(let error): print("Request failed: \(error.localizedDescription)") completion(nil) } } }
这样修改后,客户端就能正确解析出User对象了。
内容的提问来源于stack exchange,提问作者Sazzad Hissain Khan

