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

Swift中使用URLSession兑换Instagram API授权码遇异常

Swift 兑换Instagram授权码为访问令牌问题解决

问题根源

Instagram OAuth令牌兑换接口返回的是**application/x-www-form-urlencoded表单格式**,而非JSON。你看到的冗长字符串是原始的表单响应内容(例:access_token=XXX&user_id=XXX),直接用JSONDecoder解码会失败,这就是Postman正常但Swift代码出问题的核心原因。

修复步骤

1. 修正授权码截取逻辑

硬编码dropLast(2)极易截断错误,改用URLComponents解析回调URL的查询参数,更可靠:

func checkRequestForCallbackURL(request: URLRequest) -> Bool {
    guard let url = request.url, 
          url.absoluteString.starts(with: SocialAccountCredentials.instagramCallBackUrl) else {
        return true
    }
    
    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
          let code = components.queryItems?.first(where: { $0.name == "code" })?.value else {
        return true
    }
    
    print("授权码:\(code)")
    handleAuth(authToken: code)
    return false
}

2. 正确构造令牌兑换请求

兑换令牌必须发送POST请求,参数以表单编码格式传递:

func exchangeCodeForToken(code: String) {
    guard let url = URL(string: "https://api.instagram.com/oauth/access_token") else { return }
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    
    // 指定请求内容类型为表单编码
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    
    // 构造必填参数(注意需要客户端密钥client_secret)
    let parameters = [
        "client_id": SocialAccountCredentials.instagramClientId,
        "client_secret": SocialAccountCredentials.instagramClientSecret,
        "grant_type": "authorization_code",
        "redirect_uri": SocialAccountCredentials.instagramCallBackUrl,
        "code": code
    ]
    
    // 将参数编码为表单格式字符串
    request.httpBody = parameters
        .map { key, value in
            "\(key)=\(value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)"
        }
        .joined(separator: "&")
        .data(using: .utf8)
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else {
            print("请求错误:\(error!)")
            return
        }
        
        if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode != 200 {
            print("HTTP状态码:\(httpResponse.statusCode)")
            print("错误响应:\(String(data: data, encoding: .utf8)!)")
            return
        }
        
        // 解析表单格式的响应
        if let responseString = String(data: data, encoding: .utf8) {
            let responseComponents = URLComponents(string: "?\(responseString)")
            let accessToken = responseComponents?.queryItems?.first(where: { $0.name == "access_token" })?.value
            let userId = responseComponents?.queryItems?.first(where: { $0.name == "user_id" })?.value
            
            print("访问令牌:\(accessToken ?? "未获取到")")
            print("用户ID:\(userId ?? "未获取到")")
            
            // 可选:转成模型对象
            if let accessToken = accessToken, let userId = userId {
                let tokenResponse = AccessTokenResponse(accessToken: accessToken, userId: userId)
                // 后续业务处理
            }
        }
    }
    task.resume()
}

// 对应的响应模型
struct AccessTokenResponse {
    let accessToken: String
    let userId: String
}

3. 替换原handleAuth逻辑

确保handleAuth内部调用的是上面的exchangeCodeForToken方法,传入正确的授权码。

内容的提问来源于stack exchange,提问作者Muhammad Bilal Raza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 06:33:15