Swift中UrlRequest的JSON编码问题:请求体格式异常
问题:登录请求体被转为Base64编码而非预期JSON格式
我正在搭建一个API服务,编写了如下登录请求代码:
enum HttpMethod: Equatable { case get([URLQueryItem]) case put(Data?) case post(Data?) var name: String { switch self { case .get: return "GET" case .put: return "PUT" case .post: return "POST" } } } struct Request<Response> { let url: URL let method: HttpMethod var headers: [String: String] = [:] } extension Request { static func postUserLogin(email: String, password:String) -> Self { var params = [String: Any]() params["username"] = email params["password"] = password let data = params.jsonData return Request( url: URL(string: NetworkingConstants.USER_LOGIN)!, method: .post( try? JSONEncoder().encode(data) //should this be something else??? ) ) } } extension Dictionary { var jsonData: Data? { return try? JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted]) } } extension Request { var urlRequest: URLRequest { var request = URLRequest(url: url) switch method { case .post(let data), .put(let data): request.httpBody = data case let .get(queryItems): //removed for brevity default: break } request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.allHTTPHeaderFields = headers request.httpMethod = method.name return request } } extension URLSession { func decode<Value: Decodable>( _ request: Request<Value>, using decoder: JSONDecoder = .init() ) async throws -> Value { let decoded = Task.detached(priority: .userInitiated) { let (data, _) = try await self.data(for: request.urlRequest) try Task.checkCancellation() return try decoder.decode(Value.self, from: data) } return try await decoded.value } }
问题在于,发送给服务器的请求体并非预期的JSON格式:
{ "username": "me@you.com", "password": "abcdef" }
而是变成了Base64编码字符串:
"eyJwYXNzd29yZCI6ImFhYWEiLCJ1cZSI6InRhQHQuY29tIn0="
我通过以下方式发起请求:
let request: Request<UserLoginResponse> = .postUserLogin(email: "me@you.com", password: "abcdef") let response = try? await URLSession.shared.decode(request)
请求的httpBody信息如下:
▿ Optional<Data> ▿ some : 74 bytes - count : 74 ▿ pointer : 0x00007fcc74011200 - pointerValue : 140516096283136
请问我哪里出错了?
原因与修复方案
核心错误
你犯了重复编码的问题:
- 第一步通过
params.jsonData已经把字典序列化成了JSON格式的Data,这一步是正确的; - 但你又用
JSONEncoder().encode(data)去编码这个Data对象——JSONEncoder处理Data类型时,默认会将其转为Base64字符串,最终导致请求体变成了Base64编码后的内容,而非原始JSON。
修复代码
直接使用params.jsonData作为POST请求的body即可,去掉多余的JSONEncoder编码步骤:
extension Request { static func postUserLogin(email: String, password:String) -> Self { var params = [String: Any]() params["username"] = email params["password"] = password return Request( url: URL(string: NetworkingConstants.USER_LOGIN)!, method: .post(params.jsonData) ) } }
额外优化建议
- 避免强制解包
URL(string:),添加可选绑定处理,防止URL无效导致崩溃; - 建议用
Codable协议替代JSONSerialization,类型更安全:比如定义一个LoginParams结构体,直接用JSONEncoder编码,比[String:Any]更可靠。示例如下:
struct LoginParams: Encodable { let username: String let password: String } extension Request { static func postUserLogin(email: String, password:String) -> Self? { guard let url = URL(string: NetworkingConstants.USER_LOGIN) else { return nil } let params = LoginParams(username: email, password: password) let data = try? JSONEncoder().encode(params) return Request( url: url, method: .post(data) ) } }
内容的提问来源于stack exchange,提问作者soleil
相关产品推荐
相关产品推荐

