关于Retrofit请求返回同字段不同类型数据的问题

xiaoxiao2021-02-28  18

背景

使用Retrofit加Rxjava网络请求,配合FastJson进行解析,遇见问题。

案场还原

登录成功,接口返回如下

{ "Status": 200, "Ticket": "84954b2d5691438591401f0dd415f21d", "ErrorMessage": "", "User": { "Id": 53, "Mobile": "13002179439", "Name": "李乐", "Nick": "李乐", "TIcon": "https://admin-test.hcjapp.com:50000/webImgServer/upload/office/201806/04/1c1acb47-1126-469d-939b-cfbdad5ec3ac.jpg", "CompanyId": "D93A2949320A4246AA2B9BFEB3900B84", "CompanyName": "总部", "RoomNum": "201", "CardNum": "E50BB8DD", "EmployeeNum": "201002", "IsGym": true }, "Authority": {} }

登录失败,接口返回如下

{ "Status": 410, "Ticket": "-1", "ErrorMessage": "密码错误!", "User": "{}", "Authority": {} }

可以看见User字段返回类型由一个对象变成了一个String字符串。 如果执行网络请求时还是使用的User对象泛型,当登陆失败返回User类型变为字符串之后就会报解析异常。

解决方式

解决方式一言以蔽之就是自定义json解析工厂类,在其中进行二次解析,当失败时捕获异常

首先定义一个泛型类,在同字段不同类型处使用 public class LoginUserEntity<T> extends BaseModel { private int Status; private String Ticket; private String ErrorMessage; private T User; //登录成功则泛型为User private AuthorityBean Authority; } 定义登录成功时返回的User字段类型 public class User{ private int Id; private String Mobile; private String Name; private String Nick; private String TIcon; private String CompanyId; private String CompanyName; private String RoomNum; private String CardNum; private String EmployeeNum; private boolean IsGym; } 定义登录失败时返回的数据类型,这里的User字段就可以直接String public class LoginUserErrorEntity extends BaseModel { private int Status; private String Ticket; private String ErrorMessage; private String User; //登录失败,String类型 private AuthorityBean Authority; } 自定义json解析工厂类继承Converter.Factory public final class GsonDConverterFactory extends Converter.Factory { public static GsonDConverterFactory create() { return create(new Gson()); } public static GsonDConverterFactory create(Gson gson) { return new GsonDConverterFactory(gson); } private final Gson gson; private GsonDConverterFactory(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); this.gson = gson; } @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return new GsonResponseBodyConverter < >(gson, type); } @Override public Converter < ?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { TypeAdapter< ?> adapter = gson.getAdapter(TypeToken.get(type)); return new GsonRequestBodyConverter < >(gson, adapter); } } 实现 Converter<T, RequestBody> 请求体接口 final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> { private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8"); private static final Charset UTF_8 = Charset.forName("UTF-8"); private final Gson gson; private final TypeAdapter<T> adapter; GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) { this.gson = gson; this.adapter = adapter; } @Override public RequestBody convert(T value) throws IOException { Buffer buffer = new Buffer(); Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8); JsonWriter jsonWriter = gson.newJsonWriter(writer); adapter.write(jsonWriter, value); jsonWriter.close(); return RequestBody.create(MEDIA_TYPE, buffer.readByteString()); } } 重点来了!自定义类实现响应体,重写convert方法,进行二次解析。当登录成功时,返回code等于200,于是我们使用LoginUserEntity<User> 类型进行解析,当登录失败返回410时,我们使用LoginUserErrorEntity 进行解析(当然如果这里使用LoginUserEntity应该也是正确的),并抛出我们自定义的异常,一共后面的数据提取。 final class GsonResponseBodyConverter < T > implements Converter< ResponseBody, T > { private final Gson gson; private final Type type; GsonResponseBodyConverter(Gson gson, Type type) { this.gson = gson; this.type = type; } /** * 针对数据返回成功、错误不同类型字段处理 */ @Override public T convert(ResponseBody value) throws IOException { String response = value.string(); try { // 这里的type实际类型是 LoginUserEntity<User> User就是user字段的对象。 LoginUserEntity result = gson.fromJson(response, LoginUserEntity.class); int code = result.getStatus(); if (code == 200) { return gson.fromJson(response, type); } else { Log.d("HttpManager", "err==:" + response); LoginUserErrorEntity errResponse = gson.fromJson(response, LoginUserErrorEntity.class); if (code == 410) { throw new ResultException(errResponse.getErrorMessage(), code); } else { throw new ResultException(errResponse.getErrorMessage(), code); } } } finally { value.close(); } } } 附上自定义异常代码 public class ResultException extends IOException { private String errMsg; private int errCode; public ResultException(String errMsg, int errCode){ this.errMsg = errMsg; this.errCode = errCode; } public String getErrMsg() { return errMsg; } public void setErrMsg(String errMsg) { this.errMsg = errMsg; } public int getErrCode() { return errCode; } public void setErrCode(int errCode) { this.errCode = errCode; } } 使用我们自定义的json数据解析器 sRetrofit1 = new Retrofit.Builder() .client(httpClient) .baseUrl(API_LOGIN) .addConverterFactory(GsonDConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();

并且一定要保证我们的接口返回的是正确的完整的数据类型,如 LoginUserEntity<User>,否则会报错。

//登录 @POST("api/login/") @Headers("Content-Type: application/json") Observable<LoginUserEntity<User>> getAppLogin(@Body RequestBody body);

之后我们就使用retrofit加rxjava进行正常的网络流式请求。 要获取登陆失败时的数据,可以在Subscriber的onError中捕获异常,拿到数据,比如

@Override public void onError(Throwable e) { String error = ""; try { if (e instanceof SocketTimeoutException) { error = "网络连接超时,请稍后再试..."; } else if (e instanceof ConnectException) { error = "网络连接超时,请稍后再试..."; } else if (e instanceof UnknownHostException) { error = "网络连接超时,请稍后再试..."; } else { if (e instanceof ResultException) { error = ((ResultException) e).getErrMsg(); //抛出异常,抓取数据,拿到失败的errormsg } else { // error = "网络连接超时,请稍后再试..."; } } ToolAlert.showCustomShortToast(error); //alert管理 onMyError(error); //自定义的error Log.e("error", mContext.getPackageName() + "=====Error=======>" + e.toString()); e.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } finally { } }

转载请注明原文地址: https://www.6miu.com/read-2100330.html

最新回复(0)