Retrofit Post请求返回空响应,查阅Stack相关问题未获解求助
我来帮你捋捋这个问题——PostMan能正常拿到数据,说明后端接口本身没问题,问题大概率出在Retrofit的配置、请求体格式或者序列化环节,咱们一步步来排查:
1. 补全并检查Gson转换器的配置
你提到ApiClient里用了GsonConverterFactory但代码没写完,首先得确保这个配置完整,且没有遗漏OkHttpClient的基础配置:
object ApiClient { val BASE_URL = "http://MyIpaddress:3005/api/" private var retrofit: Retrofit? = null val client: Retrofit get() { if (retrofit == null) { // 基础OkHttpClient配置 val okHttpClient = OkHttpClient.Builder().build() retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) // 别漏了create()方法 .client(okHttpClient) .build() } return retrofit!! } }
如果后端返回的字段名和你的POJO字段大小写/命名不匹配,Gson会直接解析出空对象,这是非常常见的坑。
2. 检查LoginModel的序列化注解是否正确
假设PostMan里后端返回的JSON格式是这样:
{ "username": "test_user", "token": "abc_123_token" }
你的LoginModel必须保证字段名完全匹配,或者用@SerializedName注解做映射:
Java版本
public class LoginModel { // 若后端字段是user_name,这里需写成@SerializedName("user_name") @SerializedName("username") private String username; @SerializedName("token") private String token; // 务必补充对应的getter/setter方法 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } }
Kotlin版本(推荐用data class)
data class LoginModel( @SerializedName("username") val username: String?, @SerializedName("token") val token: String? )
⚠️ 重点提示:Kotlin字段一定要加?设为可空类型,如果后端返回某个字段为null,Gson会直接解析失败,导致整个对象为空。
3. 确保Retrofit接口定义和PostMan请求方式完全匹配
这是最容易踩的坑!比如PostMan用form-data或x-www-form-urlencoded提交参数,但Retrofit用@Body发送JSON,两者完全不兼容:
情况1:PostMan用JSON请求体(Raw/JSON格式)
ApiService应该这么定义:
interface ApiService { @POST("login") // 路径要和BASE_URL拼接后与PostMan地址一致 suspend fun login(@Body loginModel: LoginModel): Response<LoginModel> // 不用协程的话,替换为Call<LoginModel>即可 }
情况2:PostMan用表单提交(form-data/x-www-form-urlencoded)
需改用@FormUrlEncoded注解:
interface ApiService { @FormUrlEncoded @POST("login") suspend fun login( @Field("username") username: String, @Field("token") token: String ): Response<LoginModel> }
必须和PostMan的请求方式完全对应,否则后端接不到参数,自然返回空响应。
4. 添加OkHttp日志拦截器,查看真实请求/响应
这是排查问题的核心步骤!能帮你直观看到Retrofit实际发送的请求和PostMan的差异:
首先在app/build.gradle中添加依赖:
implementation "com.squareup.okhttp3:logging-interceptor:4.11.0"
然后修改ApiClient的OkHttpClient配置:
val loggingInterceptor = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY // 打印完整的请求头、请求体、响应头、响应体 } val okHttpClient = OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .build()
查看Logcat输出:
- 对比PostMan的请求URL、请求头(比如Content-Type)、请求体是否完全一致
- 观察后端返回的响应体:如果后端返回空JSON,问题在后端;如果返回正常JSON但Retrofit解析为空,那就是序列化的问题
5. 正确处理Retrofit响应,不要直接取body()
有时候返回空是因为响应码不是2xx,或者响应体本身为空,用Response<T>获取完整响应信息:
// 协程示例 coroutineScope.launch { try { val apiService = ApiClient.client.create(ApiService::class.java) val response = apiService.login(loginModel) if (response.isSuccessful) { val loginResult = response.body() if (loginResult == null) { // 打印原始响应内容排查 Log.d("RetrofitDebug", "Raw response body: ${response.raw().body()?.string()}") } else { // 正常处理数据 } } else { // 响应码错误,打印错误详情 Log.d("RetrofitDebug", "Request failed: ${response.code()} - ${response.message()}") } } catch (e: Exception) { // 捕获网络异常 e.printStackTrace() } }
6. 检查BASE_URL的结尾格式
你的BASE_URL是http://MyIpaddress:3005/api/,要确保结尾带有/,接口路径写login,拼接后为http://MyIpaddress:3005/api/login,和PostMan地址完全一致。如果BASE_URL没加/,接口路径需写成/login,格式要统一。
按照上面的步骤排查,重点关注日志拦截器的输出,对比PostMan的请求细节,应该能快速定位问题。
内容的提问来源于stack exchange,提问作者BraveEvidence

