1. 程式人生 > >Retrofit2 結合 Rxjava 解決返回的 JSON

Retrofit2 結合 Rxjava 解決返回的 JSON

English Version

please click here to see English version. To explain how to retrieve data from non restful response to get the right data(I don't have much time to finish English version, please wait for more detail coverage).

中文版

效果展示

rx common

本文主要介紹了使用 Retrofit2 配合 Rxjava 來處理非 restful 的網路請求結果。一般而言請求的非 REST 結果如下:

{
    "status": 1,    //請求返回狀態碼
    "msg": "請求成功",    //請求返回狀態
    "result": {    //返回結果
        "npage": 1,   //當前頁
        "pageSize": 10,   //每頁資訊條數
        "total": 1,    //查詢到的資訊條數
        "data": [{
            "phone": "010-62770334;010-62782051",    //電話
            "website": "www.tsinghua.edu.cn",    //官網
            "email": "
[email protected]
", //郵箱 "address": "北京市海淀區清華大學", //地址 "zipcode": "", //郵編 "name": "清華大學", //學校名稱 "img": "http://img.jidichong.com/school/3.png", //學校 logo 圖片 "parent": "教育部", //隸屬部門 "type": " 211 985", //學校型別 "profile": "", //簡介 "info": "院士:68 人 博士點:198 個 碩士點:181 個", //說明 "city": "北京" //所在省市 }] } }

而 Retrofit2 請求的結果一般都分為 Header 和 Body。當然使用使用 Rxjava 後,則可以實現獲取這個 result 的結果。當是單個實體的時候,在 onNext 中就直接得到這個結果,如果是一個數組的時候,則是返回 List 這種形式。

使用方法:

  1. 配置對應的外層實體,例如下面。開發中一般都非標準的 REST 都是一個數據(百度開放平臺的介面就基本都是這種形式),一個狀態碼和一個訊息。其中 data 的型別是泛型,可以在生成請求的 api 指定實際返回的型別,如果為空的情況可以使用 String。 而 code 和 message 是對應於服務端定義的程式碼 code 和返回的錯誤資訊。Demo 裡有初始值是因為找的介面沒有使用這兩個欄位,但我想模擬這類情形而設定的。如果你的後臺後臺欄位不同,你可以按需要修改這個 HttpResult 的相應欄位。

     {
             // code 為返回的狀態碼, message 為返回的訊息, 演示的沒有這兩個欄位,考慮到真實的環境中基本包含就在這裡寫定值
             private int code = 0;
                 private String message = "OK";
    
             //用來模仿 Data
             @SerializedName(value = "subjects")
             private T data;
     }
    
  2. 同 Retrofit2 一樣要定義介面,如下。這裡僅僅是有 get 的介面在 demo 裡,其它的 Put, Delete, Query 等都是一樣的。

    HttpResult裡的型別就是指定泛型 data 的具體型別。可以使用 String, JSONObject,定義的實體等等。

    另外有朋友問題訪問引數是 JSON 物件怎麼辦 (Body Paramter JSON Object)?這其實就是將你的引數設定成一個已經定義的實體,我也給出一個專案中的介面。下面的 post 就是這種方式。關於請求的 REST 方式我會在文章後面放出詳細的參考,若你不熟悉請參考這些文章。

    
             @GET("top250")
         Observable<HttpResult<List<ContentBean>>> getTopMovie(@Query("start") int start, @Query("count") int count);
    
             @POST("user/login")
         Observable<HttpResult<UserLoginBean>> postLogin(@Body UserLoginRequest request);
    
  3. 請求網路。

    直接呼叫

                         ServiceFactory.movieApi()
                             .getTopMovie(1, 10)
    

    使用一個預設的.compose(new DefaultTransformer<List<ContentBean>>())可以非常方便地進行轉化成了需要的Observable

    另外準備了常用的 subscriber,包含了網路連線的錯誤處理,例如非 200 狀態,另外是服務端(業務)錯誤的處理,預設是將錯誤編碼和錯誤資訊在控制檯和手機上輸出。 正確的情況下則必須實現。

    建議使用 Rxlifecycle 防止使用 Rxjava 記憶體洩露。

錯誤方面

定義了伺服器錯誤和連線錯誤等。主要使用了 RxJava 中的onErrorResumeNext,遇到錯誤後將錯誤通過ExceptionEngine.handleException(throwable)進行處理。

@Override
    public Observable<T> call(Observable<HttpResult<T>> responseObservable) {
        return responseObservable.map(new Func1<HttpResult<T>, T>() {
            @Override
            public T call(HttpResult<T> httpResult) {
                // 通過對返回碼進行業務判斷決定是返回錯誤還是正常取資料
//                if (httpResult.getCode() != 200) throw new RuntimeException(httpResult.getMessage());
                if (httpResult.getCode() != ErrorType.SUCCESS) throw new ServerException(httpResult.getMessage(), httpResult.getCode());
                return httpResult.getData();
            }
        }).onErrorResumeNext(new Func1<Throwable, Observable<? extends T>>() {
            @Override
            public Observable<? extends T> call(Throwable throwable) {
                //ExceptionEngine 為處理異常的驅動器
                throwable.printStackTrace();
                return Observable.error(ExceptionEngine.handleException(throwable));
            }
        });
    }

其中的 ApiException

public class ApiException extends Exception {
    // 異常處理,為速度,不必要設定 getter 和 setter
    public int code;
    public String message;

    public ApiException(Throwable throwable, int code) {
        super(throwable);
        this.code = code;
    }
}

重點在於下面這個處理。你可以再定義自己的業務相關的錯誤,目前常用的都已經有了,而業相關的錯誤大部分通過接收的資料直接顯示了。若不是直接顯示的情況,你完全可以在提供的 subscriber 裡去實現。

public class ExceptionEngine {
    //對應 HTTP 的狀態碼
    private static final int UNAUTHORIZED = 401;
    private static final int FORBIDDEN = 403;
    private static final int NOT_FOUND = 404;
    private static final int REQUEST_TIMEOUT = 408;
    private static final int INTERNAL_SERVER_ERROR = 500;
    private static final int BAD_GATEWAY = 502;
    private static final int SERVICE_UNAVAILABLE = 503;
    private static final int GATEWAY_TIMEOUT = 504;

    public static ApiException handleException(Throwable e){
        ApiException ex;
        if (e instanceof HttpException){             //HTTP 錯誤
            HttpException httpException = (HttpException) e;
            ex = new ApiException(e, ErrorType.HTTP_ERROR);
            switch(httpException.code()){
                case UNAUTHORIZED:
                    ex.message = "當前請求需要使用者驗證";
                    break;
                case FORBIDDEN:
                    ex.message = "伺服器已經理解請求,但是拒絕執行它";
                    break;
                case NOT_FOUND:
                    ex.message = "伺服器異常,請稍後再試";
                    break;
                case REQUEST_TIMEOUT:
                    ex.message = "請求超時";
                    break;
                case GATEWAY_TIMEOUT:
                    ex.message = "作為閘道器或者代理工作的伺服器嘗試執行請求時,未能及時從上游伺服器(URI 標識出的伺服器,例如 HTTP、FTP、LDAP)或者輔助伺服器(例如 DNS)收到響應";
                    break;
                case INTERNAL_SERVER_ERROR:
                    ex.message = "伺服器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理";
                    break;
                case BAD_GATEWAY:
                    ex.message = "作為閘道器或者代理工作的伺服器嘗試執行請求時,從上游伺服器接收到無效的響應";
                    break;
                case SERVICE_UNAVAILABLE:
                    ex.message = "由於臨時的伺服器維護或者過載,伺服器當前無法處理請求";
                    break;
                default:
                    ex.message = "網路錯誤";  //其它均視為網路錯誤
                    break;
            }
            return ex;
        } else if (e instanceof ServerException){    //伺服器返回的錯誤
            ServerException resultException = (ServerException) e;
            ex = new ApiException(resultException, resultException.code);
            ex.message = resultException.message;
            return ex;
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException){
            ex = new ApiException(e, ErrorType.PARSE_ERROR);
            ex.message = "解析錯誤";            //均視為解析錯誤
            return ex;
        }else if(e instanceof ConnectException || e instanceof SocketTimeoutException || e instanceof ConnectTimeoutException){
            ex = new ApiException(e, ErrorType.NETWORD_ERROR);
            ex.message = "連線失敗";  //均視為網路錯誤
            return ex;
        }
        else {
            ex = new ApiException(e, ErrorType.UNKNOWN);
            ex.message = "未知錯誤";          //未知錯誤
            return ex;
        }
    }

}

Update

2016-10-13
修正了服務端 code 沒有處理,返回為錯誤時認為是 json 實體解析問題。
`if (httpResult.getCode() != ErrorType.SUCCESS || httpResult.getCode() != ErrorType.SUCCESS)`

Thanks

相關推薦

Retrofit2 結合 Rxjava 解決返回JSON

English Versionplease click here to see English version. To explain how to retrieve data from non restful response to get the right data(I don't have much

解決返回JSON時報錯:No converter found for return value of type:

exception org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentExc

Django學習問題——解決返回JSON資料時中文出現亂碼

在嘗試用Django返回JSON資料時,一開始的views.py檔案是這樣寫的: 執行的結果是這樣的: 之後百度一下,找到相關的幾種解決方式,這是我找到的文章連結。第一種方式我嘗試了,執行後發現會

解決springmvc返回json中文亂碼

jackson ons con handle json 額外 adapter blog 設置 [email protected]/* */,這個問題上網找了很久,發現答案真是人雲亦雲,奉上我的解決方案: 解決方案一:需要導入 jackson-core-asl-1

Jquery form.js文件上傳返回JSON數據,在IE下提示下載文件的解決辦法,並對返回數據進行正確的解析

設置 解析 轉換 解決 pla esp 下載 ring 上傳 Jquery from.js插件上傳文件非常方便,但是在ie10以下的版本會彈出下載文件對話框 解決方法: 1、在服務端設置response.setContentType("text/plain"); 2、對返回

解決PHP服務端返回json字符串有特殊字符的問題

解析 clas 要求 com 切換 trac bom break 必須 1. 問題描述 在調用PHP後臺接口發現後臺接口返回的json字符串Gson一直解析不通過: List<Region> districts = null; if (!Text

SSM框架:解決後臺傳數據到前臺中文亂碼問題,使用@ResponseBody返回json 中文亂碼

tex 多人 AC 文件 進行 orm clas sha pes 場景: 在實際運用場景中,當前臺發起請求後,我們需要從後臺返回數據給前臺,這時,如果返回的數據中包含中文,則經常會出現在後臺查詢出來都是好好,但是傳輸回去就莫名的亂碼了,而且,我們明明已經在 web.xml

C#調用接口返回json數據中含有雙引號 或其他非法字符的解決辦法

方法 其他 屬性 json對象 獲取 csdn return src 解決辦法 這幾天,調用別人接口返回json數據含有特殊符號(雙引號),當轉換成json對象總是報錯, json字符格式如下 { "BOXINFO":[ {

Java後臺返回複雜資料、大資料給前端解決辦法(返回json資料分類簡化前段操作)

背景分析: 面對當前多種的業務需求和雜亂無章的資料堆放,Java後臺處理變得尤為重要。對於資訊系統開發而言,歸根到底是對資料的分類與處理。通過從伺服器獲取資料,客戶給出需求,形成怎樣的表格,多樣化的靜態圖和動態圖,各種各樣資原始檔,這些需求都不是簡單的從資料庫中取出來就可以直接使用的,往

後臺服務返回Json資料出現$ref的問題解決方案

問題出現:使用FastJson的JSONArray型別作為返回資料,當像JSONArray物件中新增JSONObject物件,而JSONObject物件中包含相同的節點資料時,FastJson會防止返回資料棧溢位的問題,自動將JSONArray中相同的節點資料使用引用方式代替,即:{"$ref"

解決SpringMVC返回JSON IE下提示下載

SpringMVC的配置檔案中: <!--註解驅動 --> <mvc:annotation-driven> <mvc:message-converters> <!-- <ref bean="stringHttpMessag

SpringMVC解決@ResponseBody返回Json的Date日期型別的轉換問題

在做專案的時候,發現後臺把Date型別的屬性以json字串的形式返回,前臺拿不到轉換後的日期格式,始終響應回去的都是long型別時間戳。 查閱資料之後找到解決方法(在springmvc的xml配置檔案下): <mvc:annotation-driven> <mv

Php如何返回json資料,前後端分離的基本解決方案

php返回json,xml,JSONP等格式的資料 返回json資料: header('Content-Type:application/json; charset=utf-8'); $arr = array('a'=>1,'b'=>2); exit(json_enco

springboot解決返回前臺json資料中文變?問題

import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context

JSON的使用、利用正則表示式解決不定型別返回Json格式

介紹 作為移動端開發者,和伺服器的通訊是少不了了。下面我總結一我在開發中遇到有關json的問題 一般的Json 在我剛入門的時候,在公司的寫程式碼當時的網路返回也不復雜,10個欄位以內,基本上熟悉Json的固定格式,手動寫Bean接收資料,只要用心欄位

解決springboot json返回值 null 處理 為 ""

package com.powerpeak.adstation.config; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessi

SpringMVC在返回JSON資料時出現406錯誤解決方案

       在SpringMVC框架的使用中常常會使用@ResponseBody註解,修飾“處理器”(Controller的方法),這樣在處理器在返回完畢後,就不走邏輯檢視,而是將返回的物件轉成JSON字串響應給客戶端,但這種操作有時會出現406錯誤。

SpringMVC 使用@ResponseBody返回json 中文亂碼問題解決

這確實是個蛋疼的問題,Spring中解析字串的轉換器預設編碼居然是ISO-8859-1 既然找到問題了,那就必須想辦法改過來,不同版本的Spring好像方法還不一樣,網上不少說的都是Spring3.*的,現在Spring4早都出來了 更改方式可以參考

解決WebApi,Mvc返回json資料遇見日期帶T

我們用webapi自帶的json序列化時,遇見資料庫datetime型別的日期中間會多加一個T,就像2017-09-09 22:11:26.267,而經過自帶的json處理後會變成2017-09-09T22:11:26.267。 解決辦法: 我們可以看到自帶的json有三

解決後臺返回JSON格式資料,IE瀏覽器提示下載的問題

解決後臺返回JSON格式資料,IE瀏覽器提示下載的問題 問題描述 在近期的一個專案中,使用前後端分離開發,後端使用SpringMVC向前臺返回JSON資料的時候,其他瀏覽器都可以正常顯示,唯獨IE瀏覽器讓人蛋疼不已,每次都要下載下來才能開啟,真的很讓人抓狂,於是老套路上網一頓查資料,