如何在Kotlin中使用Retrofit2获取响应URL(Spotify授权场景)
在Android Kotlin中处理Spotify授权码流程的回调参数(suspend版)
核心说明
Spotify授权码流程中,用户授权后的回调参数是通过App的Deep Link/Scheme带回你的应用,而非Retrofit发起的HTTP请求响应。所以你不需要通过Retrofit获取响应URL,直接从Android的Intent中解析回调URI的查询参数即可。以下是具体实现步骤:
1. 配置App的Deep Link回调
首先在AndroidManifest.xml中为处理回调的Activity添加Intent Filter,匹配你在Spotify开发者后台配置的回调URI:
<activity android:name=".SpotifyAuthCallbackActivity" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- 替换成你自己的回调URI Scheme --> <data android:scheme="yourapp" android:host="spotify-auth" android:path="/callback" /> </intent-filter> </activity>
2. 在回调Activity中解析参数(suspend函数实现)
在回调Activity中,从Intent中提取回调URI,解析出code、state等关键参数,再用suspend函数处理后续的Token请求:
class SpotifyAuthCallbackActivity : AppCompatActivity() { // 初始化Retrofit的Spotify API实例 private val spotifyApi by lazy { Retrofit.Builder() .baseUrl("https://accounts.spotify.com/") .addConverterFactory(GsonConverterFactory.create()) .build() .create(SpotifyApi::class.java) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launch { intent.data?.let { uri -> handleAuthCallback(uri) } finish() } } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) intent?.data?.let { uri -> lifecycleScope.launch { handleAuthCallback(uri) } } finish() } private suspend fun handleAuthCallback(callbackUri: Uri) { // 解析查询参数 val authCode = callbackUri.getQueryParameter("code") val state = callbackUri.getQueryParameter("state") val error = callbackUri.getQueryParameter("error") when { error != null -> { // 处理授权错误,比如用户拒绝授权 Log.e("SpotifyAuth", "Authorization failed: $error") } authCode != null -> { // 用授权码请求Access Token runCatching { val tokenResponse = spotifyApi.fetchAccessToken( code = authCode, redirectUri = "yourapp://spotify-auth/callback", clientId = "YOUR_SPOTIFY_CLIENT_ID", clientSecret = "YOUR_SPOTIFY_CLIENT_SECRET" ) // 保存Token到本地,比如DataStore或SharedPreferences saveTokenToLocal(tokenResponse) }.onFailure { Log.e("SpotifyAuth", "Failed to fetch token", it) } } } } private suspend fun saveTokenToLocal(tokenResponse: TokenResponse) { // 实现Token持久化逻辑 } }
3. Retrofit接口定义(suspend版)
定义获取Access Token的接口,使用suspend函数,无需Call<>或.enqueue():
interface SpotifyApi { @FormUrlEncoded @POST("api/token") suspend fun fetchAccessToken( @Field("grant_type") grantType: String = "authorization_code", @Field("code") code: String, @Field("redirect_uri") redirectUri: String, @Field("client_id") clientId: String, @Field("client_secret") clientSecret: String ): TokenResponse } data class TokenResponse( @SerializedName("access_token") val accessToken: String, @SerializedName("token_type") val tokenType: String, @SerializedName("expires_in") val expiresIn: Long, @SerializedName("refresh_token") val refreshToken: String )
特殊场景:用Retrofit获取重定向URL
如果你确实需要通过Retrofit获取授权请求的重定向URL(比如自动处理跳转),可以通过Response.raw()获取OkHttp的原始响应,进而拿到最终URL:
interface SpotifyAuthRedirectApi { @GET("authorize") suspend fun getAuthRedirect( @Query("client_id") clientId: String, @Query("response_type") responseType: String = "code", @Query("redirect_uri") redirectUri: String, @Query("scope") scope: String, @Query("state") state: String ): Response<Void> } // 调用示例 suspend fun getRedirectUrl(): String? { val response = spotifyAuthRedirectApi.getAuthRedirect( clientId = "YOUR_CLIENT_ID", redirectUri = "yourapp://spotify-auth/callback", scope = "user-read-private user-read-email", state = "random_unique_state" ) return if (response.isRedirect) { response.raw().request.url.toString() } else { null } }
注意:这种方式仅适用于无用户交互的重定向场景,Spotify的授权页面需要用户手动登录授权,所以更推荐用WebView/Custom Tab加载授权URL,再通过Deep Link回调处理参数。
内容的提问来源于stack exchange,提问作者RetroGhost
相关产品推荐
相关产品推荐

