Retrofit 2中PUT请求空响应与错误响应的兼容处理方案
解决Retrofit 2中PUT请求成功无Body、失败有JSON错误体的问题
这个场景我之前也碰到过,确实有点棘手——Retrofit默认的解析逻辑要么拿不到错误响应体,要么成功时因为空Body直接抛出EOF异常。给你两个实用的解决方案,看你偏好哪种:
方案一:用Response<ResponseBody>手动处理(推荐)
这是最直接的方式,不需要额外自定义类,完全手动控制解析逻辑:
首先定义接口:
@PUT("/path/to/get") Call<Response<ResponseBody>> getMyData(/* 你的参数 */);
然后在回调里处理成功和失败的情况:
call.enqueue(new Callback<Response<ResponseBody>>() { @Override public void onResponse(Call<Response<ResponseBody>> call, Response<Response<ResponseBody>> response) { if (response.isSuccessful()) { // 成功逻辑:因为无Body,直接跳过解析 handleSuccess(); } else { // 失败时解析错误响应体 try { // 这里用你项目里的JSON解析库,比如Gson ErrorResponse errorResponse = new Gson() .fromJson(response.errorBody().charStream(), ErrorResponse.class); handleError(errorResponse); } catch (IOException e) { // 处理解析失败的情况(比如错误响应不是JSON格式) handleParseError(e); } finally { // 记得关闭ResponseBody,避免资源泄漏 if (response.errorBody() != null) { response.errorBody().close(); } } } } @Override public void onFailure(Call<Response<ResponseBody>> call, Throwable t) { // 处理网络失败、超时等情况 handleNetworkFailure(t); } });
这种方式的好处是逻辑清晰,完全由你掌控解析过程,不会出现Retrofit自动解析导致的异常。
方案二:自定义Converter自动处理空Body
如果你希望接口定义更简洁(比如直接返回Call<ErrorResponse>),可以自定义一个Converter,让它在成功且无Body时不抛出异常:
步骤1:实现自定义ConverterFactory
public class EmptyBodyConverterFactory extends Converter.Factory { private final Gson gson; private EmptyBodyConverterFactory(Gson gson) { this.gson = gson; } public static EmptyBodyConverterFactory create() { return create(new Gson()); } public static EmptyBodyConverterFactory create(Gson gson) { return new EmptyBodyConverterFactory(gson); } @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { // 获取Retrofit默认的Gson转换器作为委托 Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations); return new EmptyBodyConverter<>(delegate, type); } private static class EmptyBodyConverter<T> implements Converter<ResponseBody, T> { private final Converter<ResponseBody, T> delegate; private final Type targetType; EmptyBodyConverter(Converter<ResponseBody, T> delegate, Type targetType) { this.delegate = delegate; this.targetType = targetType; } @Override public T convert(ResponseBody value) throws IOException { try { // 先尝试用默认转换器解析 return delegate.convert(value); } catch (EOFException e) { // 捕获EOF异常(说明Body为空),默认只有成功时才会空Body // 直接返回null,避免抛出异常 value.close(); return null; } } } }
步骤2:配置Retrofit时添加这个Converter
注意要把自定义的Converter放在GsonConverterFactory之前:
Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(EmptyBodyConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build();
步骤3:定义接口并使用
@PUT("/path/to/get") Call<ErrorResponse> getMyData(/* 你的参数 */);
然后在回调里处理:
call.enqueue(new Callback<ErrorResponse>() { @Override public void onResponse(Call<ErrorResponse> call, Response<ErrorResponse> response) { if (response.isSuccessful()) { // 成功时response.body()为null,直接处理成功逻辑 handleSuccess(); } else { // 失败时解析错误响应体 try { ErrorResponse errorResponse = new Gson() .fromJson(response.errorBody().charStream(), ErrorResponse.class); handleError(errorResponse); } catch (IOException e) { handleParseError(e); } } } @Override public void onFailure(Call<ErrorResponse> call, Throwable t) { handleNetworkFailure(t); } });
这个方案的好处是接口定义更干净,但需要额外维护自定义的Converter类,适合项目中有多个类似接口的场景。
内容的提问来源于stack exchange,提问作者Jono
相关产品推荐
相关产品推荐

