Retrofit Response.isSuccessful() 返回 false 如何解决?
明确结论
Response.isSuccessful() 返回 false,是因为服务器响应了 HTTP 402 Payment Required。
Retrofit 只把 200~299 范围内的状态码视为成功。这个问题发生在 JSON 反序列化之前,通常与 Topic 或 TopicResponse 的定义无关,而是接口或托管平台拒绝了请求。
对于 HTTP 错误响应:
response.body()通常是null- 错误内容在
response.errorBody()中 - Retrofit 仍会调用
onResponse() - 网络中断、DNS 失败、超时等情况通常才会进入
onFailure()
为什么会返回 402
402 Payment Required 是服务器返回的状态码,不是 Retrofit 生成的。原因可能是:
- 接口服务的付费、配额或账单出现问题
- 托管平台暂停了应用
- 接口要求认证、订阅或其他请求参数
- 后端代码主动返回了
402 - 网关、代理或第三方服务拒绝了请求
仅凭现有日志无法判断具体原因,还需要查看后端日志、托管平台状态和接口的认证要求。
可以直接访问接口,或使用 curl 测试:
curl -i https://quizziyapa.herokuapp.com/getTopics
如果该命令同样返回 402,就能确认问题出在服务端或托管平台,而不是 Android 客户端。
修正 Retrofit 接口声明
当前代码使用了原始类型 Call,Retrofit 无法确定目标类型。应将接口声明为 Call<TopicResponse>:
public interface IUserApi {
@GET("getTopics")
Call<TopicResponse> getTop();
}
调用代码也需要使用完整泛型:
Call<TopicResponse> call = api.getTop();
call.enqueue(new Callback<TopicResponse>() {
@Override
public void onResponse(
Call<TopicResponse> call,
Response<TopicResponse> response
) {
// 处理响应
}
@Override
public void onFailure(Call<TopicResponse> call, Throwable t) {
Log.e("Ayush", "Request failed", t);
}
});
修改后,当服务器返回成功的 JSON 响应时,Gson 可以将其解析为 TopicResponse。不过,这项修改不会让 HTTP 402 变成成功响应,服务端问题仍需单独处理。
正确处理成功响应和错误响应
读取 response.body() 前,应先检查状态码:
Call<TopicResponse> call = api.getTop();
call.enqueue(new Callback<TopicResponse>() {
@Override
public void onResponse(
Call<TopicResponse> call,
Response<TopicResponse> response
) {
Log.d("Ayush", "HTTP status: " + response.code());
Log.d("Ayush", "HTTP message: " + response.message());
if (response.isSuccessful()) {
TopicResponse topicResponse = response.body();
if (topicResponse == null) {
Log.e("Ayush", "Successful response has an empty body");
return;
}
Topic[] topics = topicResponse.getTopics();
if (topics == null) {
Log.e("Ayush", "Response does not contain topics");
return;
}
for (Topic topic : topics) {
Log.d(
"Ayush",
"id=" + topic.getId()
+ ", topic=" + topic.getTopic()
+ ", imageUrl=" + topic.getImageUrl()
);
}
} else {
String errorContent = "";
try {
ResponseBody errorBody = response.errorBody();
if (errorBody != null) {
errorContent = errorBody.string();
}
} catch (IOException e) {
Log.e("Ayush", "Failed to read error body", e);
}
Log.e(
"Ayush",
"HTTP error " + response.code()
+ " " + response.message()
+ ", body=" + errorContent
);
}
}
@Override
public void onFailure(Call<TopicResponse> call, Throwable t) {
Log.e("Ayush", "Network or conversion failure", t);
}
});
response.errorBody().toString() 打印的是类似 okhttp3.ResponseBody$1@647c751 的对象标识,并非服务器返回的实际内容。要读取响应内容,应调用:
response.errorBody().string()
ResponseBody.string() 会消费响应流,通常只能读取一次,所以应先把结果保存到变量中。
Model 可以这样定义
JSON 顶层是一个包含 topics 数组的对象,因此可以使用 TopicResponse 包装:
public class TopicResponse {
@SerializedName("topics")
private Topic[] topics;
public Topic[] getTopics() {
return topics;
}
}
Topic 中可以显式标注对应的 JSON 字段,避免字段名称调整后影响解析:
public class Topic {
@SerializedName("_id")
private int id;
@SerializedName("topic")
private String topic;
@SerializedName("imageUrl")
private String imageUrl;
public int getId() {
return id;
}
public String getTopic() {
return topic;
}
public String getImageUrl() {
return imageUrl;
}
}
常规的 Gson 反序列化不要求这些 Model 提供包含全部参数的构造方法。
如果希望使用集合,也可以把数组改为:
public class TopicResponse {
@SerializedName("topics")
private List<Topic> topics;
public List<Topic> getTopics() {
return topics;
}
}
需要检查的服务端配置
修正客户端泛型后,还要检查以下内容:
- 确认接口目前可以正常访问并返回
2xx。 - 查看后端日志,找出返回
402的服务或代码。 - 检查托管服务的账单、配额和应用运行状态。
- 确认接口是否要求
Authorization、API Key、Cookie 或其他 Header。 - 确认请求路径和 HTTP 方法是
GET /getTopics。 - 确认成功响应的
Content-Type是application/json。 - 确认成功响应符合
{"topics":[...]}结构。
如果接口要求认证,可以根据后端规范添加 Header,例如:
public interface IUserApi {
@GET("getTopics")
Call<TopicResponse> getTop(
@Header("Authorization") String authorization
);
}
调用时:
api.getTop("Bearer " + token);
是否需要这个 Header、它的名称以及值的生成方式,都应以后端接口规范为准,不能只根据 402 推断。
日志中的 retrofit2.OkHttpCall$NoContentResponseBody 也不是接口返回的 JSON。Retrofit 处理原始响应体时会使用内部占位对象。成功响应的业务数据应从 response.body() 获取,错误信息则从 response.errorBody() 获取。
解决这个问题需要分别处理服务端和客户端。服务端必须先停止返回 402,让接口正常响应 2xx;客户端则应使用 Call<TopicResponse> 和 Callback<TopicResponse>,由 Retrofit 通过 Gson 解析 JSON。
备注:内容仅供参考。
