前言

直接上数据结构:

{

"code": 200,

"data": {

"id": "1",

"name": "name1",

"stargazers_count": 1

},

"msg": "请求成功"

}

上面的数据结构是一般比较简单而常见的数据结构,在正确的情况下我们只关心data里面的数据,错误的情况下我们关心code和msg提示,而区分这两种情况又要不断的写大量的样板代码,这不是首选。所以就有了两种方法:

通过定义泛型T来处理

自定义ResponseBodyConverter实现统一处理

首先讲讲泛型T的处理方式,基本如下:

public class BaseDataModel {

private int code;

private T data;

private String msg;

public int getCode() {

return code;

}

public void setCode(int code) {

this.code = code;

}

public T getData() {

return data;

}

public void setData(T data) {

this.data = data;

}

public String getMsg() {

return msg;

}

public void setMsg(String msg) {

this.msg = msg;

}

public boolean isSuccessful() {

return code == 200;

}

}

使用也比较简单,只需要将泛型替换为相应的实体类就行。但是这也有一个不好的地方就是一旦项目中对接的数据结构变得繁杂的时候,就需要不断的顶部相应的实体了,还是比较麻烦,后面会讲到,所以我们还是使用自定义ResponseBodyConverter实现统一处理。

实现

分析

我在使用Retrofit的时候,一般是使用的是GsonConverterFactory,而GsonConverterFactory则是负责将我们的提交的数据进行序列化和将返回的数据进行反序列化的,所以只要我们分析完成其中的代码,看明白他是怎么对数据进行反序列化的,那么我们就能做到统一的数据转换了。

源码目录

GsonConverter源码目录

从上图我们可以看到,其实只有三个类,分别是:GsonConverterFactory、GsonRequestBodyConverter和GsonResponseBodyConverter。

GsonConverterFactory 主入口,负责联通GsonRequestBodyConverter和GsonResponseBodyConverter

GsonRequestBodyConverter 负责将注解为@Body的对象序列化为Json字符串。

GsonResponseBodyConverter 负责将返回的json字符串反序列化为对象。

其中GsonResponseBodyConverter的源码内容如下:

final class GsonResponseBodyConverter implements Converter {

private final Gson gson;

private final TypeAdapter adapter;

GsonResponseBodyConverter(Gson gson, TypeAdapter adapter) {

this.gson = gson;

this.adapter = adapter;

}

@Override public T convert(ResponseBody value) throws IOException {

// 这里就是对返回结果进行处理

JsonReader jsonReader = gson.newJsonReader(value.charStream());

try {

T result = adapter.read(jsonReader);

if (jsonReader.peek() != JsonToken.END_DOCUMENT) {

throw new JsonIOException("JSON document was not fully consumed.");

}

return result;

} finally {

value.close();

}

}

}

从上面的源码可以看到,convert方法中其实就是通过Gson进行看一次序列化而已,而ResponseBody提供了string方法,直接将返回结果转换为了字符串,那么我们就可以在中间加一道工序,将其中的数据进行相关的判断处理,再进行返回。

编码

首先将GsonConverter的源码copy过来,然后改个名字,如下:

HandlerErrorGsonConvert

为了模拟不同的数据接口,硬编码了以下三种数据结构。

数据结构·1

{

"code": 200,

"message": "成功,但是没有数据",

"data": []

}

数据结构·2

{

"code": -1,

"message": "这里是接口返回的:错误的信息,抛出错误信息提示!",

"data": []

}

数据结构·3

{

"code": 401,

"message": "这里是接口返回的:权限不足,请重新登录!",

"data": []

}

从上面三种数据结构分析,我们需要在code为200的情况下直接序列化data,其它情况下抛出code和msg,所以这里我们还需要定义一个Exception类来承载错误信息。

创建Exception类

Exception类如下,主要是对一些和后端约定的状态码进行转义和包裹。

public class NetErrorException extends IOException {

private Throwable exception;

private int mErrorType = NO_CONNECT_ERROR;

private String mErrorMessage;

/*无连接异常*/

public static final int NoConnectError = 1;

/**

* 数据解析异常

*/

public static final int PARSE_ERROR = 0;

/**

* 无连接异常

*/

public static final int NO_CONNECT_ERROR = 1;

/*网络连接超时*/

public static final int SocketTimeoutError = 6;

/**

* 无法连接到服务

*/

public static final int ConnectExceptionError = 7;

/**

* 服务器错误

*/

public static final int HttpException = 8;

/**

* 登陆失效

*/

public static final int LOGIN_OUT = 401;

/**

* 其他

*/

public static final int OTHER = -99;

/**

* 没有网络

*/

public static final int UNOKE = -1;

/**

* 无法找到

*/

public static final int NOT_FOUND = 404;

/*其他*/

public NetErrorException(Throwable exception, int mErrorType) {

this.exception = exception;

this.mErrorType = mErrorType;

}

public NetErrorException(String message, Throwable cause) {

super(message, cause);

}

public NetErrorException(String message, int mErrorType) {

super(message);

this.mErrorType = mErrorType;

this.mErrorMessage = message;

}

@Override

public String getMessage() {

if (!TextUtils.isEmpty(mErrorMessage)) {

return mErrorMessage;

}

switch (mErrorType) {

case PARSE_ERROR:

return "数据解析异常";

case NO_CONNECT_ERROR:

return "无连接异常";

case OTHER:

return mErrorMessage;

case UNOKE:

return "当前无网络连接";

case ConnectExceptionError:

return "无法连接到服务器,请检查网络连接后再试!";

case HttpException:

try {

if (exception.getMessage().equals("HTTP 500 Internal Server Error")) {

return "服务器发生错误!";

}

} catch (Exception e) {

e.printStackTrace();

}

if (exception.getMessage().contains("Not Found"))

return "无法连接到服务器,请检查网络连接后再试!";

return "服务器发生错误";

}

try {

return exception.getMessage();

} catch (Exception e) {

return "未知错误";

}

}

/**

* 获取错误类型

*/

public int getErrorType() {

return mErrorType;

}

}

统一订阅处理

由于这里使用的是RxJava,需要自定义一个Subscriber来对Convert抛出的Exception进行捕获,也需要对其它Exception进行捕获和包裹,防止发生错误后直接崩溃,代码不多,如下:

public abstract class ApiSubscriber extends ResourceSubscriber {

@Override

public void onError(Throwable e) {

NetErrorException error = null;

if (e != null) {

// 对不是自定义抛出的错误进行解析

if (!(e instanceof NetErrorException)) {

if (e instanceof UnknownHostException) {

error = new NetErrorException(e, NetErrorException.NoConnectError);

} else if (e instanceof JSONException || e instanceof JsonParseException) {

error = new NetErrorException(e, NetErrorException.PARSE_ERROR);

} else if (e instanceof SocketTimeoutException) {

error = new NetErrorException(e, NetErrorException.SocketTimeoutError);

} else if (e instanceof ConnectException) {

error = new NetErrorException(e, NetErrorException.ConnectExceptionError);

} else {

error = new NetErrorException(e, NetErrorException.OTHER);

}

} else {

error = new NetErrorException(e.getMessage(), NetErrorException.OTHER);

}

}

// 回调抽象方法

onFail(error);

}

/**

* 回调错误

*/

protected abstract void onFail(NetErrorException error);

}

修改HandlerErrorGsonResponseBodyConverter

这一步其实没什么难度,只是在convert方法中提前将ResponseBody.string()取出来,通过最简单的JSONObject来判断code,为200则返回data,否则抛出自定义错误。详细代码如下:

final class HandlerErrorGsonResponseBodyConverter implements Converter {

private final TypeAdapter adapter;

/**模拟的假数据*/

private final List mockResult;

private final Random random;

HandlerErrorGsonResponseBodyConverter(TypeAdapter adapter) {

this.random = new Random();

this.adapter = adapter;

mockResult = new ArrayList<>();

mockResult.add("{\"code\":200,\"message\":\"成功,但是没有数据\",\"data\":[]}");

mockResult.add("{\"code\":-1,\"message\":\"这里是接口返回的:错误的信息,抛出错误信息提示!\",\"data\":[]}");

mockResult.add("{\"code\":401,\"message\":\"这里是接口返回的:权限不足,请重新登录!\",\"data\":[]}");

}

@Override

public T convert(ResponseBody value) throws IOException {

// 这里就是对返回结果进行处理

String jsonString = value.string();

try {

// 这里为了模拟不同的网络请求,所以采用了本地字符串的格式然后进行随机选择判断结果。

int resultIndex = random.nextInt(mockResult.size() + 1);

if (resultIndex == mockResult.size()) {

return adapter.fromJson(jsonString);

} else {

// 这里模拟不同的数据结构

jsonString = mockResult.get(resultIndex);

Log.e("TAG", "这里进行了返回结果的判断");

// ------------------ JsonObject 只做了初略的判断,具体情况自定

JSONObject object = new JSONObject(jsonString);

int code = object.getInt("code");

if (code != 200) {

throw new NetErrorException(object.getString("message"), code);

}

return adapter.fromJson(object.getString("data"));

}

} catch (JSONException e) {

e.printStackTrace();

throw new NetErrorException("数据解析异常", NetErrorException.PARSE_ERROR);

} finally {

value.close();

}

}

}

如何调用

主要是有以下两个地方:

创建Retrofit的时候将addConverterFactory换成自定义的HandlerErrorGsonConverterFactory.create()

RxJava的subscribeWith换成自定义的ApiSubscriber。

部分代码如下:

......

retrofit = new Retrofit.Builder()

.baseUrl("https://api.github.com/")

.addConverterFactory(HandlerErrorGsonConverterFactory.create()) // 这里使用的是用自己自定义的转换器

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())

.build();

service = retrofit.create(GitHubService.class);

......

mResultTv.setText("创建请求................\n");

disposable = service.listRxJava2FlowableRepos("aohanyao", "owner")

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribeWith(new ApiSubscriber>() {

@Override

public void onNext(List repos) {

mResultTv.append("请求成功,repoCount:" + repos.size() + ":\n");

for (Repo repo : repos) {

mResultTv.append("repoName:" + repo.getName() + " star:" + repo.getStargazers_count() + "\n");

}

}

@Override

protected void onFail(NetErrorException error) {

mResultTv.append("请求失败" + error.getMessage() + "................\n");

}

@Override

public void onComplete() {

mResultTv.append("请求成功................\n");

}

});

演示

HandlerResponseError

结束

这里只写了对返回状态码和错误的统一处理,并未对不同的数据结构进行处理,下一篇将对不同的数据结构进行统一处理。

微信公众号

AndroidRookie

java修改异常状态码_RxJava2 + Retrofit2 完全指南 之 统一状态码/Exception处理相关推荐

  1. php中http有哪些状态码,http状态码是什么意思?http常用状态码有哪些

    作为开发人员对http状态码都是很熟悉的,只有对这些http状态码熟悉,才能找到问题的关键所在,那么http状态码是什么意思?http常用状态码有哪些?接下来php中文网就向大家介绍一下http状态码 ...

  2. android如何获取网络的状态码,Android RxJava+Retrofit网络异常、状态码统一处理

    Android RxJava+Retrofit 网络异常捕获.状态码统一处理 前言 近来使用RxJava+Retrofit进行开发,在项目中遇到这样一个需求,联网请求获得数据异常时,需要将对应的Mes ...

  3. python状态码409_生产环境常见的HTTP状态码列表

    200 - OK,服务器成功返回网页 - Standard response for successful HTTP requests. 301 - Moved Permanently(永久跳转),请 ...

  4. Http状态码一览(从100-500之间所有状态码)

    # ***HTTP状态码列表 *** 状态码 状态码英文名称 中文描述 100 Continue 继续.客户端应继续其请求 101 Switching Protocols 切换协议.服务器根据客户端的 ...

  5. ajax请求提示html状态码302,快速解决ajax请求出错状态码为0的问题

    今天在使用 ajax 向后台请求数据时出现错误,提示状态码为 0 ,后台采用的是 spring mvc 架构. 状态码为0是什么意思呢?查找了下,原来它意味着 (未初始化)即没有调用到send()方法 ...

  6. 302状态码_你见过 HTTP 哪些状态码?

    ❝ 好久没有写技术文章,今天在四川广元无事,总结一篇.附一张今天早上在嘉陵江遇见的白鹡鸰 (不是我拍的) ❞ 白鹡鸰 101 Switch Protocol 200 Ok 201 Created 20 ...

  7. 关于html状态码错误的是,关于 HTTP 307 状态码

    关于各种 HTTP 状态码可以说是虽不曾深入,但也算有所了解,而在写关于什么是 HSTS 安全协议的时候,猛然间发现 不是很常见的 307 状态码,因为我们平常见的都是 301,302,就连 303 ...

  8. python接口返回状态码,Python脚本接口返回正常,状态码405

    最近些Python接口脚本时调用post接口时返回结果正常,状态码却不对. ,代码如下: 此接口完成的功能为新增一个角色,角色添加成功返回角色的id信息,但是状态码为405,405是Method No ...

  9. ajax返回状态码为零,ajax - HTTP请求返回状态码0时的含义是什么?

    ajax - HTTP请求返回状态码0时的含义是什么? JavaScript网络调用(例如fetch或XMLHttpRequest)或任何其他类型的HTTP网络请求在HTTP状态代码为0时失败意味着什 ...

最新文章

  1. 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler...
  2. Banner图的播放
  3. 笨办法学R编程(2)
  4. CSS背景颜色转换(自动生成IE8以下的filter,兼容IE9)
  5. 如何提高SSD的使用寿命
  6. Swift - 自定义单元格实现微信聊天界面
  7. python常用函数的用法_python中常用函数整理
  8. 微服务间保持事务一致性
  9. openmp官方源码_Faiss 源码解析
  10. 关于scanf对换行的吸收
  11. 视频换脸AISWAP技术示例
  12. Java 理论与实践: 修复 Java 内存模型,第 1 部分
  13. speedoffice表格的外框线怎么设置?
  14. python基础坑点
  15. 《此生,若你安好.便是晴天》 -- @ShinePug
  16. 基本数据类型的隐式转换
  17. 独享云虚拟主机、共享云虚拟主机、云服务器 ECS 区别
  18. flutter截取字符串_Flutter Dart List.map() 获取下标
  19. 揭开人工智能的神秘面纱
  20. Java快速开发之代码生成器

热门文章

  1. Python程序异常处理:try、except、else、finally,捕获指定异常类型、捕获多个异常类型、捕获所有异常类型、捕获异常信息、异常的传递、raise抛出自定义异常
  2. OpenCV之颜色空间转换(笔记10)
  3. python创建透明窗体_python – PyQt5:使用不透明的子项创建半透明窗口
  4. asp activex 读取服务器上的文件,webshell中上传asp文件调用服务器ActiveX控件溢出获取shell...
  5. html快照抓取,请教前端实现获取dom元素快照的方法
  6. 为什么手机上传图片这么慢 前端_解决BootStrap Fileinput手机图片上传显示旋转问题_心病_前端开发者...
  7. OpenShift 4 Hands-on Lab (12) 使用配置参数和环境变量
  8. (二)为自动化MLOps设置GitHub、Docker和Google Cloud Platform
  9. ONNX系列五 --- 在C#中使用可移植的ONNX AI模型
  10. (转)ASP.NET Core 企业开发架构概述