Retrofit统一处理服务器返回参数

想了下还是写下这篇博客,去年遇到的一个坑。
关于这个服务器返回的请求状态,我以前用的是这种方式:
给 Android 开发者的 RxJava 详解
RxJava+Retrofit,在联网返回后如何先进行统一的判断?

这种方式对服务器返回的Json格式有要求,必须是这种样子的:

{
  "status": 1,
  "msg": "message",
  "data":{}
}

然后用这样的一个类去接收

public class Result<T> {
  public int status;
  public String msg;
  public T data;
}

我也觉得以上的方式很好用,直到我遇到了这种Json:

{
  "status": 1,
  "msg": "message",
  "data":{},
  "otherData":{},
  "adData":{}
}

上面给出的链接的方式是指定一个泛型作为接收data的实体对象,但是这里有三种data实体…… 我感觉我踩坑了

一、使用自定义ConvertAdapter

我又搜索了好久,看到了另一种方式,那就是Retrofit可以使用自定义的Converter,它的作用是可以将接受到的json转换成实体返回给我们。

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(ApiService.API_BASE_URL)
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create()) // 就是这里
        .addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
        .build(); 

一般情况下我们都使用GsonConverterFactory,当请求到json后,Retrofit就会调用GsonConverter将json转成我们需要的实体。

GsonConverterFactory一共只有三个类,并且代码量很少,如下:

public final class GsonConverterFactory extends Converter.Factory {
  /**
   * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
   * decoding from JSON (when no charset is specified by a header) will use UTF-8.
   */
  public static GsonConverterFactory create() {
    return create(new Gson());
  }

  /**
   * Create an instance using {@code gson} for conversion. Encoding to JSON and
   * decoding from JSON (when no charset is specified by a header) will use UTF-8.
   */
  public static GsonConverterFactory create(Gson gson) {
    return new GsonConverterFactory(gson);
  }

  private final Gson gson;

  private GsonConverterFactory(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    this.gson = gson;
  }

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

  @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);
  }
}
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}
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());
  }
}

当我们发出一个POST JSON请求(直接用Retrofit post一个实体给服务器,请求的时候会自动将我们的实体转成Json)的时候,Retrofit就会调用GsonRequestBodyConverter的Convert方法将我们的实体转换成json。
而接受服务器返回的数据时,Retrofit会调用GsonResponseBodyConverter将Json数据转换成我们需要的实体。

####既然如此,我们可以在Converter解析json的时候就做服务器参数的统一处理

我是将GsonConverterFactory的三个类拷贝出来修改了一下:
GsonConverterFactory和RequestBodyConverter几乎没有任何修改,我们需要更改类是GsonResponseBodyConverter,因为它才是将服务器的数据转换成实体了,我们在转换的过程中做统一处理。

/**
 * Created by AItsuki on 2016/12/16.
 */
final class ResponseBodyConverter<T> implements Converter<ResponseBody, T> {

    private static final int FAILURE = 0;       // 失败 提示失败msg
    private static final int SUCCESS = 1;       // 成功
    private static final int TOKEN_EXPIRE = 2;  // token过期
    private static final int SERVER_EXCEPTION = 3;  // 服务器异常

    private final Gson gson;
    private final TypeAdapter<T> adapter;

    ResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        String json = value.string();
        try {
            verify(json);
            return adapter.read(gson.newJsonReader(new StringReader(json)));
        } finally {
            value.close();
        }
    }

    private void verify(String json) {
        Result result = gson.fromJson(json, Result.class);
        if (result.state != SUCCESS) {
            switch (result.state) {
                case FAILURE:
                case SERVER_EXCEPTION:
                    throw new ApiException(result.msg);
                case TOKEN_EXPIRE:
                    throw new TokenExpireException(result.msg);
                default:
                    throw new UndefinedStateException();
            }
        }
    }
}
public class Result {
    public String msg;
    public int state;
}

将json解析成实体之前,我们先手动解析state和msg两个字段,根据状态抛出自定义异常即可。
但是我们的用于接收实体的对象也比较丑,一个类套着一个内部类!例如:

public class User {
    public Data data;

    public static class Data {
      public String username;
      public String phone;
      public String address;
    }
}

这也是一个蛋疼的地方,但是我没有更好的解决方式。
而处理自定义异常和上面给出的两个链接的方式也没什么不同,自定义一个Subscriber在OnError中统一接收处理即可。

二、建议

这是一个解决方案,但是将统一处理改成这种方式之前,我更推荐去找写接口文档或者服务器的哥们谈谈……
别给我们返回这种奇葩的Json:

{
  "status": 1,
  "msg": "message",
  "data":{},
  "otherData":{},
  "adData":{}
}

或者这种更加奇葩的:

{
    "reason": "成功的返回",
    "result": {
        "stat": "1",
        "data": {}
    }
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
如果您使用Retrofit发送POST请求@Body方式处理数据库参数,可以按照以下步骤进行操作: 1. 创建一个Java类,用于存储参数。例如,如果您要向数据库添加一条用户记录,可以创建一个名为User的Java类,其属性包括用户名,密码等等。 ```java public class User { private String username; private String password; public User(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public String getPassword() { return password; } } ``` 2. 在Retrofit接口中定义POST请求,使用@Body注解将User对象作为请求体发送到服务器。 ```java public interface ApiService { @POST("users") Call<Void> createUser(@Body User user); } ``` 3. 在应用中使用Retrofit对象创建接口实例,并调用createUser方法。 ```java // 创建Retrofit对象 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://example.com/api/") .addConverterFactory(GsonConverterFactory.create()) .build(); // 创建接口实例 ApiService apiService = retrofit.create(ApiService.class); // 创建User对象 User user = new User("username", "password"); // 发送POST请求 Call<Void> call = apiService.createUser(user); call.enqueue(new Callback<Void>() { @Override public void onResponse(Call<Void> call, Response<Void> response) { // 请求成功 } @Override public void onFailure(Call<Void> call, Throwable t) { // 请求失败 } }); ``` 上述代码将向服务器发送一条POST请求,将User对象作为请求体发送到服务器。如果请求成功,服务器将会将User对象存储到数据库中。如果请求失败,将会调用onFailure方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值