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

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

请问我哪里出错了?


原因与修复方案

核心错误

你犯了重复编码的问题:

  1. 第一步通过params.jsonData已经把字典序列化成了JSON格式的Data,这一步是正确的;
  2. 但你又用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 05:23:20