Android使用Retrofit修改后臺返回的不規(guī)范json數(shù)據(jù)

1 .背景

做過app開發(fā)的都知道,一般公認的接口數(shù)據(jù)格式如下

{
    "status": 200,
    "data": {
        "sex": "男",
        "userId": 123456,
        "userName": "張三"
    },
    "msg": "登錄成功"
}

當?shù)卿涘e誤的時候,返回的數(shù)據(jù)格式如下:

{
    "status": 400,
    "data": {},//或者null
    "msg": "賬號不存在"
}

Android開發(fā)人員一般會在項目框架中統(tǒng)一處理解析后臺返回的數(shù)據(jù),而不需要每個接口手動解析數(shù)據(jù)了.比如,我們用Retrofit框架,請求接口時候,定義如下:

 //登錄
    @POST("login")
    Observable<Response<LoginBean>> toLogin(@Body RequestBody body);

定義全局統(tǒng)一的接收數(shù)據(jù)的javaBean

public class Response<T> {

    private int status;  //狀態(tài)碼  0:失敗  1:成功
    private String msg; // 顯示的信息
    private T messageList; // 業(yè)務(wù)數(shù)據(jù)

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMsg() {
        return msg == null ? "未知原因" : msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getResults() {
        return messageList;
    }

    public void setResults(T results) {
        this.messageList = results;
    }

    @Override
    public String toString() {
        return "Response{" +
                "status=" + status +
                ", msg='" + msg + '\'' +
                ", results=" + messageList.toString() +
                '}';
    }
}

全局統(tǒng)一處理網(wǎng)絡(luò)請求,根據(jù)狀態(tài)碼區(qū)分業(yè)務(wù).
前面說了一堆正常操作,現(xiàn)在問題來了.....當?shù)卿浾_的時候,后臺返回的數(shù)據(jù)是正常的,但是失敗的時候,返回的數(shù)據(jù)如下

{
    "messageList": [
        {
            "exceptionClass": "com.zdcx.base.common.exception.AppException",
            "messageBody": "用戶已存在",
            "messageForDeveloper": "MST00002",
            "messageId": "MST00002",
            "messageInstanceId": "3CVHZF33WFCC7KAFL5JZCLoHUY",
            "messageLevel": "ERROR",
            "messageSubject": "用戶已存在",
            "path": "/api/cust/sms"
        }
    ],
    "status": 400
}

什么? messageList在正確的時候是個對象,在失敗的時候,確是個數(shù)組???你讓我怎么接??于是乎,找后臺,讓他們修改為統(tǒng)一的格式...遇到后臺好還好,不好的,比如我們的,一句話: 框架就是這樣封裝的,我改不了.....我內(nèi)心一萬頭草泥馬呼嘯而過....好吧,你不解決,我自己解決吧.....解決方案如下

2.方案一

retrofit接口中統(tǒng)一用Object接收,這兒在框架中可以統(tǒng)一處理錯誤的情況,但是status=200的時候,就得自己手動解析成對應(yīng)的javaBean了..

 //登錄
    @POST("login")
    Observable<Response<Object>> toLogin(@Body RequestBody body);

但是這樣還得在回調(diào)中每次手動解析javaBean,也很麻煩啊...能不能像標準格式一樣,我只關(guān)心正確的業(yè)務(wù)數(shù)據(jù),回調(diào)過去直接是解析好的JavaBean呢?于是乎,方案二出來了

3.方案二: 使用OkHttp中的Interceptor

通過攔截器,攔截后臺返回的數(shù)據(jù),然后我們只需要判斷status,如果200,則直接返回response,如果不是,則拋出異常,此時異常會回調(diào)在OnError(本人項目是Rxjava+retrofit)中.攔截器代碼如下:

public abstract class ResponseBodyInterceptor implements Interceptor {

  @NotNull
  @Override
  public Response intercept(@NotNull Chain chain) throws IOException {
    Request request = chain.request();
    String url = request.url().toString();
    Response response = chain.proceed(request);
    ResponseBody responseBody = response.body();
    if (responseBody != null) {
      long contentLength = responseBody.contentLength();
      BufferedSource source = responseBody.source();
      source.request(Long.MAX_VALUE);
      Buffer buffer = source.getBuffer();

      if ("gzip".equals(response.headers().get("Content-Encoding"))) {
        GzipSource gzippedResponseBody = new GzipSource(buffer.clone());
        buffer = new Buffer();
        buffer.writeAll(gzippedResponseBody);
      }

      MediaType contentType = responseBody.contentType();
      Charset charset;
      if (contentType == null || contentType.charset(StandardCharsets.UTF_8) == null) {
        charset = StandardCharsets.UTF_8;
      } else {
        charset = contentType.charset(StandardCharsets.UTF_8);
      }

      if (charset != null && contentLength != 0L) {
        return intercept(response,url, buffer.clone().readString(charset));
      }
    }
    return response;
  }

  abstract Response intercept(@NotNull Response response,String url, String body);
}
 

我們只需要繼承該攔截器,然后處理自己的業(yè)務(wù)邏輯就行了,示例如下:

/**
 * Created by admin
 * Created Time: 2020/3/5 16:22
 * Description: 自己解析錯誤信息,并構(gòu)造成標準json格式 body就是后臺返回的json
*  PS:  怎么解析根據(jù)自己業(yè)務(wù)來,我是解析我后臺給我的錯誤數(shù)據(jù)....
 */
public class HandleErrorInterceptor extends ResponseBodyInterceptor {
    @Override
    Response intercept(@NotNull Response response, String url, String body) {
        try {
            JSONObject jsonObject = new JSONObject(body);
            int status = jsonObject.optInt("status");
            if (status != 200) {
                String errorMsg = jsonObject.getJSONArray("messageList").getJSONObject(0).getString("messageBody");
                throw new MyException(status, errorMsg);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return response;
    }
}

MyException的代碼如下

public class MyException extends RuntimeException {
    private int httpCode;
    private String errMsg;

    public int getHttpCode() {
        return httpCode;
    }


    public String getErrMsg() {
        return errMsg;
    }

    public MyException(int httpCode, String message) {
        super(message);
        this.httpCode = httpCode;
        this.errMsg = message;
    }
}

為啥繼承RuntimeException 而不是HTTPException或者直接Exception呢? 其實我本來是想繼承HTTPException的,但是發(fā)現(xiàn)編譯器直接報錯....這就需要了解RuntimeException 和Exception的區(qū)別了,不懂的同學(xué)可以查一下...最終OkHttp拋出的該異常,會回調(diào)在OnError中,代碼如下:

/**
    * 統(tǒng)一的網(wǎng)絡(luò)請求
    *
    * @param observable          被觀察者
    * @param <T>                 網(wǎng)絡(luò)返回的數(shù)據(jù)
    * @param compositeDisposable 用于取消網(wǎng)絡(luò)請求
    */
   public <T, K extends BasePresenter> void request(Observable<Response<T>> observable,
                                                    final CompositeDisposable compositeDisposable, final BaseView<K> view,
                                                    final CallBackListener<T> listener) {

       if (observable == null || compositeDisposable == null || view == null || listener == null) {
           return;
       }
       observable.subscribeOn(Schedulers.io())
               .observeOn(AndroidSchedulers.mainThread())
               .subscribe(new Observer<Response<T>>() {
                   @Override
                   public void onSubscribe(Disposable d) {
                       compositeDisposable.add(d);
                   }

                   @Override
                   public void onNext(Response<T> response) {
                          //回調(diào)成功,業(yè)務(wù)邏輯省略....
                   }

                   @Override
                   public void onError(Throwable e) {

                   // 此處會回調(diào)剛才我們自定義MyException.....
           
                       if (e instanceof MyException) { 
                           listener.onError(((MyException) e).getErrMsg());
                           if (((MyException) e).getHttpCode() == 403) {
                               toLogin();
                           }
                           return;
                       }
                       listener.onError(e.getMessage());
                   }

                   @Override
                   public void onComplete() {

                   }
               });
   }

到此,問題就解決了....此外,自定義攔截器中包含有請求的url,我們可以根據(jù)url來定向的修改某個接口的數(shù)據(jù)啊,從此徹底擺脫與后臺的各種撕逼吧...永遠不要跟有些人爭吵,這樣只會拉低你的智商....學(xué)會自己動手解決各種問題吧.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庐杨,一起剝皮案震驚了整個濱河市专普,隨后出現(xiàn)的幾起案子叠殷,更是在濱河造成了極大的恐慌,老刑警劉巖牛欢,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逸绎,死亡現(xiàn)場離奇詭異惹恃,居然都是意外死亡,警方通過查閱死者的電腦和手機棺牧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門巫糙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颊乘,你說我怎么就攤上這事参淹。” “怎么了疲牵?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵承二,是天一觀的道長。 經(jīng)常有香客問我纲爸,道長亥鸠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任识啦,我火速辦了婚禮负蚊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颓哮。我一直安慰自己家妆,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布冕茅。 她就那樣靜靜地躺著伤极,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姨伤。 梳的紋絲不亂的頭發(fā)上哨坪,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音乍楚,去河邊找鬼当编。 笑死,一個胖子當著我的面吹牛徒溪,可吹牛的內(nèi)容都是我干的忿偷。 我是一名探鬼主播金顿,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鲤桥!你這毒婦竟也來了揍拆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤芜壁,失蹤者是張志新(化名)和其女友劉穎礁凡,沒想到半個月后高氮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慧妄,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年剪芍,在試婚紗的時候發(fā)現(xiàn)自己被綠了塞淹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡罪裹,死狀恐怖饱普,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情状共,我是刑警寧澤套耕,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站峡继,受9級特大地震影響冯袍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜碾牌,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一康愤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧舶吗,春花似錦征冷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腹侣,卻和暖如春叔收,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筐带。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工今穿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伦籍。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓蓝晒,卻偏偏與公主長得像腮出,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子芝薇,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容