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

Kotlin中Retrofit实现带JWT返回的POST登录接口问题

Fixing Retrofit Login Handling for 200/403 Responses in Kotlin

Alright, let's work through this Retrofit + Kotlin login challenge you're hitting up against. From what you shared, the issue is almost certainly tied to how Retrofit handles different HTTP status codes and their response bodies—since your API returns similar but distinct payloads for 200 (success with token) and 403 (error with null token and detailed code). Here's how to fix it step by step:

1. Simplify Your DTOs (Use a Single Response Model)

Instead of two separate DTOs, create one unified LoginResponse that accounts for both success and error cases. Since the only difference is whether token is present or null, making that field nullable covers both scenarios:

data class LoginResponse(
    val token: String?, // Nullable for 403 responses
    val code: String    // Contains success code or error details
)

// Your credentials DTO stays the same
data class LoginCredentials(
    val username: String,
    val password: String
)

2. Update Your Retrofit Service Interface

By default, Retrofit throws an HttpException for non-2xx status codes (like 403). To avoid this and handle responses manually, have your service return a Response<LoginResponse> instead of the raw DTO:

interface AuthService {
    @POST("login") // Replace with your actual endpoint path
    suspend fun login(@Body credentials: LoginCredentials): Response<LoginResponse>
}

3. Handle Responses in Your Login Logic

Now you can explicitly check the status code and parse the response body (or error body) accordingly. Here's how to implement this in a suspend function:

suspend fun performLogin(username: String, password: String) {
    val authService = Retrofit.Builder()
        .baseUrl("YOUR_BASE_URL") // Add your base URL
        .addConverterFactory(GsonConverterFactory.create()) // Or Moshi, etc.
        .build()
        .create(AuthService::class.java)

    val credentials = LoginCredentials(username, password)
    val response = authService.login(credentials)

    when {
        response.isSuccessful -> {
            // Handle 200 OK response
            response.body()?.let { loginResponse ->
                loginResponse.token?.let { jwtToken ->
                    // Proceed with JWT processing (store it, use it for auth, etc.)
                    println("Login successful! Token: $jwtToken, Code: ${loginResponse.code}")
                } ?: run {
                    // Edge case: 200 but token is null (unlikely per your API spec)
                    println("Login succeeded but no token received")
                }
            } ?: println("Empty response body for successful login")
        }
        response.code() == 403 -> {
            // Handle 403 Forbidden response
            response.errorBody()?.let { errorBody ->
                val errorResponse = Gson().fromJson(errorBody.string(), LoginResponse::class.java)
                println("Login failed: ${errorResponse.code}")
                // Show error to user, log details, etc.
            } ?: println("Empty error body for 403 response")
        }
        else -> {
            // Handle other status codes (404, 500, etc.)
            println("Unexpected status code: ${response.code()}")
        }
    }
}

4. Key Notes to Avoid Pitfalls

  • Converter Factory Compatibility: Ensure your chosen converter (Gson, Moshi, etc.) supports nullable fields. Gson handles this out of the box, but if you're using Moshi, make sure you've enabled kotlinx.serialization or marked the token field as nullable in your adapter.
  • Error Body Parsing: Always call string() on the error body only once—it consumes the stream, so storing it in a variable first is safe if you need to reuse it.
  • Coroutine Safety: Since we're using suspend functions, make sure you're calling performLogin from a coroutine scope (like viewModelScope in Android or a custom CoroutineScope).

This approach matches how your iOS implementation likely handles the API, since it lets you explicitly manage both success and error responses without relying on Retrofit's default exception behavior.

内容的提问来源于stack exchange,提问作者netshark1000

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:11:26