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

如何使用Retrofit2和RxAndroid处理条件响应?

解决Retrofit2+RxAndroid处理204成功/422错误响应的问题

这个问题我之前也碰到过,Completable确实在这种场景下有点局限——它只关注操作完成或失败,没法直接拿到422返回的错误响应体。不过有两种靠谱的解决方式,看你更倾向哪种:

方案一:保留Completable,在错误分支解析HttpException

Retrofit会把非2xx的响应包装成HttpException抛出,我们可以在onError回调里捕获这个异常,然后解析它携带的响应体:

首先,先定义一个对应422错误JSON的实体类(根据你的API返回结构调整):

public class ErrorResponse {
    private String detail; // 假设API返回的全局错误描述
    private Map<String, List<String>> fieldErrors; // 如果是表单字段级验证错误

    // Getter & Setter
    public String getDetail() { return detail; }
    public void setDetail(String detail) { this.detail = detail; }
    public Map<String, List<String>> getFieldErrors() { return fieldErrors; }
    public void setFieldErrors(Map<String, List<String>> fieldErrors) { this.fieldErrors = fieldErrors; }
}

然后在订阅Completable的时候处理错误:

apiService.send(audit)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        () -> {
            // 请求成功(204)的逻辑
            Toast.makeText(context, "提交成功", Toast.LENGTH_SHORT).show();
        },
        throwable -> {
            if (throwable instanceof HttpException) {
                HttpException httpException = (HttpException) throwable;
                if (httpException.code() == 422) {
                    try {
                        // 解析错误响应体
                        ErrorResponse errorResponse = new Gson().fromJson(
                            httpException.response().errorBody().string(),
                            ErrorResponse.class
                        );
                        // 展示具体错误信息,优先全局描述,没有则用默认提示
                        String errorMsg = errorResponse.getDetail() != null 
                            ? errorResponse.getDetail() 
                            : "数据验证失败,请检查输入";
                        Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
                    } catch (IOException e) {
                        e.printStackTrace();
                        Toast.makeText(context, "解析错误信息失败", Toast.LENGTH_SHORT).show();
                    }
                } else {
                    // 处理其他HTTP错误,比如401未授权、500服务器错误等
                    Toast.makeText(context, "请求错误:" + httpException.code(), Toast.LENGTH_SHORT).show();
                }
            } else {
                // 处理网络异常、超时等非HTTP响应错误
                Toast.makeText(context, "网络异常,请稍后重试", Toast.LENGTH_SHORT).show();
            }
        }
    );

⚠️ 注意:errorBody().string()只能调用一次,因为ResponseBody是流对象,调用后会自动关闭,不要重复调用。

方案二:更换返回类型为Single<Response>,更灵活处理状态码

如果想更清晰地区分“成功响应”“HTTP错误”和“网络错误”,可以把接口的返回类型从Completable改成Single<Response<Void>>,这样能手动处理所有状态码:

首先修改Retrofit接口方法:

@POST("/api/v1/audits/")
Single<Response<Void>> send(@Body Audit audit);

然后订阅的时候处理:

apiService.send(audit)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        response -> {
            if (response.isSuccessful()) {
                // 204属于2xx范围,isSuccessful()会返回true
                Toast.makeText(context, "提交成功", Toast.LENGTH_SHORT).show();
            } else {
                if (response.code() == 422) {
                    try {
                        ErrorResponse errorResponse = new Gson().fromJson(
                            response.errorBody().string(),
                            ErrorResponse.class
                        );
                        Toast.makeText(context, errorResponse.getDetail(), Toast.LENGTH_SHORT).show();
                    } catch (IOException e) {
                        e.printStackTrace();
                        Toast.makeText(context, "解析错误信息失败", Toast.LENGTH_SHORT).show();
                    }
                } else {
                    Toast.makeText(context, "请求错误:" + response.code(), Toast.LENGTH_SHORT).show();
                }
            }
        },
        throwable -> {
            // 这里只处理网络异常、连接超时等非HTTP响应的错误
            Toast.makeText(context, "网络异常,请稍后重试", Toast.LENGTH_SHORT).show();
        }
    );

这种方式的好处是把HTTP错误(比如422、404)和网络错误(比如无网、超时)分开处理,逻辑更清晰,也方便扩展处理其他状态码。

总结

  • 如果想保持代码简洁,用方案一即可,只在错误分支处理422的情况;
  • 如果需要更灵活的状态码处理,或者要区分不同类型的错误,方案二更合适。

内容的提问来源于stack exchange,提问作者László Soós

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:42:36