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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:02:50