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
相关产品推荐
相关产品推荐

