網絡請求異常大概有哪些?php
在獲取數據的流程中,訪問接口和解析數據時都有可能會出錯,咱們能夠經過攔截器在這兩層攔截錯誤。java
最簡單的處理方式,直接對返回的throwable進行類型判斷處理git
//請求,對throwable進行判斷 ServiceHelper.getInstance() .getModelResult(param1, param2) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<Model>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { if(e instanceof HttpException){ //獲取對應statusCode和Message HttpException exception = (HttpException)e; String message = exception.response().message(); int code = exception.response().code(); }else if(e instanceof SSLHandshakeException){ //接下來就是各類異常類型判斷... }else if(e instanceof ...){ }... } @Override public void onNext(Model model) { if(model.getCode != CODE_SUCCESS){ int code = model.getCode(); switch (code){ case CODE_TOKEN_INVALID: ex.setDisplayMessage("從新登錄"); break; case CODE_NO_OTHER: ex.setDisplayMessage("其餘狀況"); break; case CODE_SHOW_TOAST: ex.setDisplayMessage("吐司服務器返回的提示"); break; case CODE_NO_MISSING_PARAMETER: ex.setDisplayMessage("缺乏參數,用log記錄服務器提示"); break; default: ex.setDisplayMessage(message); break; } }else{ //正常處理邏輯 } } });
爲了避免改變之前的代碼結構,那麼如何作纔可以完全解耦呢?通常狀況下使用Retrofit網絡請求框架,會有回調方法,以下所示:github
package retrofit2; public interface Callback<T> { void onResponse(Call<T> var1, Response<T> var2); void onFailure(Call<T> var1, Throwable var2); }
無論之前代碼封裝與否,都但願一句代碼便可實現網絡請求攔截處理邏輯。那麼這個時候,我是怎麼處理的呢?面試
public class ResponseData<T> { private int code; private String message; private T t; public int getCode() { return code; } public String getMessage() { return message; } public T getT() { return t; } } new Callback<ResponseData<HomeBlogEntity>>(){ @Override public void onResponse(Call<ResponseData<HomeBlogEntity>> call, Response<ResponseData<HomeBlogEntity>> response) { int code = response.body().getCode(); String message = response.body().getMessage(); HomeBlogEntity t = response.body().getT(); if (code!= CODE_SUCCESS){ //網絡請求成功200,不過業務層執行服務端制定的異常邏輯 ExceptionUtils.serviceException(code,message); } else { //網絡請求成功,業務邏輯正常處理 } } @Override public void onFailure(Call call, Throwable throwable) { ExceptionUtils.handleException(throwable); } };
第一步:定義請求接口網絡層失敗的狀態碼segmentfault
/**
*/ private static final int BAD_REQUEST = 400; private static final int UNAUTHORIZED = 401; private static final int FORBIDDEN = 403; private static final int NOT_FOUND = 404; private static final int METHOD_NOT_ALLOWED = 405; private static final int REQUEST_TIMEOUT = 408; private static final int CONFLICT = 409; private static final int PRECONDITION_FAILED = 412; 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; ```
第二步,接口請求成功,業務層失敗,服務端定義異常狀態碼服務器
/** * 服務器定義的狀態嗎 * 好比:登陸過時,提醒用戶從新登陸; * 添加商品,可是服務端發現庫存不足,這個時候接口請求成功,服務端定義業務層失敗,服務端給出提示語,客戶端進行吐司 * 請求接口,參數異常或者類型錯誤,請求code爲200成功狀態,不過給出提示,這個時候客戶端用log打印服務端給出的提示語,方便快遞查找問題 * 其餘狀況,接口請求成功,可是服務端定義業務層須要吐司服務端返回的對應提示語 */ /** * 徹底成功 */ private static final int CODE_SUCCESS = 0; /** * Token 失效 */ public static final int CODE_TOKEN_INVALID = 401; /** * 缺乏參數 */ public static final int CODE_NO_MISSING_PARAMETER = 400400; /** * 其餘狀況 */ public static final int CODE_NO_OTHER = 403; /** * 統一提示 */ public static final int CODE_SHOW_TOAST = 400000;
第三步,自定義Http層的異常和服務器定義的異常類markdown
public class HttpException extends Exception { private int code; private String displayMessage; public HttpException(Throwable throwable, int code) { super(throwable); this.code = code; } public void setDisplayMessage(String displayMessage) { this.displayMessage = displayMessage; } public String getDisplayMessage() { return displayMessage; } public int getCode() { return code; } } public class ServerException extends RuntimeException { public int code; public String message; public int getCode() { return code; } public void setCode(int code) { this.code = code; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
第四步,統一處理異常邏輯以下所示網絡
/** * 這個能夠處理服務器請求成功,可是業務邏輯失敗,好比token失效須要從新登錄
*/ public static void serviceException(int code , String content){ if (code != CODE_SUCCESS){ ServerException serverException = new ServerException(); serverException.setCode(code); serverException.setMessage(content); handleException(serverException); } } /** * 這個是處理網絡異常,也能夠處理業務中的異常 * @param e e異常 */ public static void handleException(Throwable e){ HttpException ex; //HTTP錯誤 網絡請求異常 好比常見404 500之類的等 if (e instanceof retrofit2.HttpException){ retrofit2.HttpException httpException = (retrofit2.HttpException) e; ex = new HttpException(e, ErrorCode.HTTP_ERROR); switch(httpException.code()){ case BAD_REQUEST: case UNAUTHORIZED: case FORBIDDEN: case NOT_FOUND: case METHOD_NOT_ALLOWED: case REQUEST_TIMEOUT: case CONFLICT: case PRECONDITION_FAILED: case GATEWAY_TIMEOUT: case INTERNAL_SERVER_ERROR: case BAD_GATEWAY: case SERVICE_UNAVAILABLE: //均視爲網絡錯誤 default: ex.setDisplayMessage("網絡錯誤"+httpException.code()); break; } } else if (e instanceof ServerException){ //服務器返回的錯誤 ServerException resultException = (ServerException) e; int code = resultException.getCode(); String message = resultException.getMessage(); ex = new HttpException(resultException, ErrorCode.SERVER_ERROR); switch (code){ case CODE_TOKEN_INVALID: ex.setDisplayMessage("token失效"); //下面這裏能夠統一處理跳轉登陸頁面的操做邏輯 break; case CODE_NO_OTHER: ex.setDisplayMessage("其餘狀況"); break; case CODE_SHOW_TOAST: ex.setDisplayMessage("吐司"); break; case CODE_NO_MISSING_PARAMETER: ex.setDisplayMessage("缺乏參數"); break; default: ex.setDisplayMessage(message); break; } } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException){ ex = new HttpException(e, ErrorCode.PARSE_ERROR); //均視爲解析錯誤 ex.setDisplayMessage("解析錯誤"); }else if(e instanceof ConnectException){ ex = new HttpException(e, ErrorCode.NETWORK_ERROR); //均視爲網絡錯誤 ex.setDisplayMessage("鏈接失敗"); } else if(e instanceof java.net.UnknownHostException){ ex = new HttpException(e, ErrorCode.NETWORK_ERROR); //網絡未鏈接 ex.setDisplayMessage("網絡未鏈接"); } else if (e instanceof SocketTimeoutException) { ex = new HttpException(e, ErrorCode.NETWORK_ERROR); //網絡未鏈接 ex.setDisplayMessage("服務器響應超時"); } else { ex = new HttpException(e, ErrorCode.UNKNOWN); //未知錯誤 ex.setDisplayMessage("未知錯誤"); } String displayMessage = ex.getDisplayMessage(); //這裏直接吐司日誌異常內容,注意正式項目中必定要注意吐司合適的內容 ToastUtils.showRoundRectToast(displayMessage); } ```
第五步,如何調用app
@Override public void onError(Throwable e) { //直接調用便可 ExceptionUtils.handleException(e); }
以下所示
public class ExceptionUtils { /* * 在使用Retrofit+RxJava時,咱們訪問接口,獲取數據的流程通常是這樣的:訂閱->訪問接口->解析數據->展現。
* * 在獲取數據的流程中,訪問接口和解析數據時都有可能會出錯,咱們能夠經過攔截器在這兩層攔截錯誤。 * 1.在訪問接口時,咱們不用設置攔截器,由於一旦出現錯誤,Retrofit會自動拋出異常。 * 2.在解析數據時,咱們設置一個攔截器,判斷Result裏面的code是否爲成功,若是不成功,則要根據與服務器約定好的錯誤碼來拋出對應的異常。 * 3.除此之外,爲了咱們要儘可能避免在View層對錯誤進行判斷,處理,咱們必須還要設置一個攔截器,攔截onError事件,而後使用ExceptionHandler,讓其根據錯誤類型來分別處理。 */ /** * 對應HTTP的狀態碼 */ private static final int BAD_REQUEST = 400; private static final int UNAUTHORIZED = 401; private static final int FORBIDDEN = 403; private static final int NOT_FOUND = 404; private static final int METHOD_NOT_ALLOWED = 405; private static final int REQUEST_TIMEOUT = 408; private static final int CONFLICT = 409; private static final int PRECONDITION_FAILED = 412; 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; /** * 服務器定義的狀態嗎 * 好比:登陸過時,提醒用戶從新登陸; * 添加商品,可是服務端發現庫存不足,這個時候接口請求成功,服務端定義業務層失敗,服務端給出提示語,客戶端進行吐司 * 請求接口,參數異常或者類型錯誤,請求code爲200成功狀態,不過給出提示,這個時候客戶端用log打印服務端給出的提示語,方便快遞查找問題 * 其餘狀況,接口請求成功,可是服務端定義業務層須要吐司服務端返回的對應提示語 */ /** * 徹底成功 */ private static final int CODE_SUCCESS = 0; /** * Token 失效 */ public static final int CODE_TOKEN_INVALID = 401; /** * 缺乏參數 */ public static final int CODE_NO_MISSING_PARAMETER = 400400; /** * 其餘狀況 */ public static final int CODE_NO_OTHER = 403; /** * 統一提示 */ public static final int CODE_SHOW_TOAST = 400000; /** * 這個能夠處理服務器請求成功,可是業務邏輯失敗,好比token失效須要從新登錄 * @param code 自定義的code碼 */ public static void serviceException(int code , String content){ if (code != CODE_SUCCESS){ ServerException serverException = new ServerException(); serverException.setCode(code); serverException.setMessage(content); handleException(serverException); } } /** * 這個是處理網絡異常,也能夠處理業務中的異常 * @param e e異常 */ public static void handleException(Throwable e){ HttpException ex; //HTTP錯誤 網絡請求異常 好比常見404 500之類的等 if (e instanceof retrofit2.HttpException){ retrofit2.HttpException httpException = (retrofit2.HttpException) e; ex = new HttpException(e, ErrorCode.HTTP_ERROR); switch(httpException.code()){ case BAD_REQUEST: case UNAUTHORIZED: case FORBIDDEN: case NOT_FOUND: case METHOD_NOT_ALLOWED: case REQUEST_TIMEOUT: case CONFLICT: case PRECONDITION_FAILED: case GATEWAY_TIMEOUT: case INTERNAL_SERVER_ERROR: case BAD_GATEWAY: case SERVICE_UNAVAILABLE: //均視爲網絡錯誤 default: ex.setDisplayMessage("網絡錯誤"+httpException.code()); break; } } else if (e instanceof ServerException){ //服務器返回的錯誤 ServerException resultException = (ServerException) e; int code = resultException.getCode(); String message = resultException.getMessage(); ex = new HttpException(resultException, ErrorCode.SERVER_ERROR); switch (code){ case CODE_TOKEN_INVALID: ex.setDisplayMessage("從新登錄"); break; case CODE_NO_OTHER: ex.setDisplayMessage("其餘狀況"); break; case CODE_SHOW_TOAST: ex.setDisplayMessage("吐司"); break; case CODE_NO_MISSING_PARAMETER: ex.setDisplayMessage("缺乏參數"); break; default: ex.setDisplayMessage(message); break; } } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException){ ex = new HttpException(e, ErrorCode.PARSE_ERROR); //均視爲解析錯誤 ex.setDisplayMessage("解析錯誤"); }else if(e instanceof ConnectException){ ex = new HttpException(e, ErrorCode.NETWORK_ERROR); //均視爲網絡錯誤 ex.setDisplayMessage("鏈接失敗"); } else if(e instanceof java.net.UnknownHostException){ ex = new HttpException(e, ErrorCode.NETWORK_ERROR); //網絡未鏈接 ex.setDisplayMessage("網絡未鏈接"); } else if (e instanceof SocketTimeoutException) { ex = new HttpException(e, ErrorCode.NETWORK_ERROR); //網絡未鏈接 ex.setDisplayMessage("服務器響應超時"); } else { ex = new HttpException(e, ErrorCode.UNKNOWN); //未知錯誤 ex.setDisplayMessage("未知錯誤"); } String displayMessage = ex.getDisplayMessage(); //這裏直接吐司日誌異常內容,注意正式項目中必定要注意吐司合適的內容 ToastUtils.showRoundRectToast(displayMessage); } } ```