网络请求异常拦截优化

简介: 目录介绍01.网络请求异常分类02.开发中注意问题03.原始的处理方式04.如何减少代码耦合性05.异常统一处理步骤06.完成版代码展示好消息博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇...

目录介绍

  • 01.网络请求异常分类
  • 02.开发中注意问题
  • 03.原始的处理方式
  • 04.如何减少代码耦合性
  • 05.异常统一处理步骤
  • 06.完成版代码展示

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

01.网络请求异常分类

  • 网络请求异常大概有哪些?

    • 第一种:访问接口异常,比如404,500等异常,出现这类异常,Retrofit会自动抛出异常。
    • 第二种:解析数据异常,数据体发生变化可能会导致这个问题。
    • 第三种:其他类型异常,比如服务器响应超时异常,链接失败异常,网络未连接异常等等。
    • 第四种:网络请求成功,但是服务器定义了异常状态,比如token失效,参数传递错误,或者统一给提示(这个地方比较拗口,比如购物app,你购买n件商品请求接口成功,code为200,但是服务器发现没有这么多商品,这个时候就会给你一个提示,然后客户端拿到这个进行吐司)

02.开发中注意问题

  • 在获取数据的流程中,访问接口和解析数据时都有可能会出错,我们可以通过拦截器在这两层拦截错误。

    • 1.在访问接口时,我们不用设置拦截器,因为一旦出现错误,Retrofit会自动抛出异常。比如,常见请求异常404,500,503等等。为了方便后期排查问题,这个可以在debug环境下打印日志就可以。
    • 2.在解析数据时,我们设置一个拦截器,判断Result里面的code是否为成功,如果不成功,则要根据与服务器约定好的错误码来抛出对应的异常。比如,token失效后跳转登录页面,禁用同账号登陆多台设备,缺少参数,参数传递异常等等。
    • 3.除此以外,为了我们要尽量避免在View层对错误进行判断,处理,我们必须还要设置一个拦截器,拦截onError事件,然后使用ExceptionUtils,让其根据错误类型来分别处理。

03.原始的处理方式

  • 最简单的处理方式,直接对返回的throwable进行类型判断处理

    //请求,对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{
        //正常处理逻辑
    }
    }
          });
    

04.如何减少代码耦合性

  • 为了不改变以前的代码结构,那么如何做才能够彻底解耦呢?一般情况下使用Retrofit网络请求框架,会有回调方法,如下所示:

    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);
        }
    };

05.异常统一处理步骤

  • 第一步:定义请求接口网络层失败的状态码

    /**
     * 对应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打印服务端给出的提示语,方便快递查找问题
    • 比如,其他情况,接口请求成功,但是服务端定义业务层需要吐司服务端返回的对应提示语
    /**
     * 服务器定义的状态吗
     * 比如:登录过期,提醒用户重新登录;
     *      添加商品,但是服务端发现库存不足,这个时候接口请求成功,服务端定义业务层失败,服务端给出提示语,客户端进行吐司
     *      请求接口,参数异常或者类型错误,请求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层的异常和服务器定义的异常类

    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失效需要重新登陆
     * @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("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);
    }
  • 第五步,如何调用

    @Override
    public void onError(Throwable e) {
        //直接调用即可
        ExceptionUtils.handleException(e);
    }

06.完成版代码展示

  • 如下所示

    public class ExceptionUtils {
    
        /*
         * 在使用Retrofit+RxJava时,我们访问接口,获取数据的流程一般是这样的:订阅->访问接口->解析数据->展示。
         * 如上所说,异常和错误本质是一样的,因此我们要尽量避免在View层对错误进行判断,处理。
         *
         * 在获取数据的流程中,访问接口和解析数据时都有可能会出错,我们可以通过拦截器在这两层拦截错误。
         * 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);
        }
    }

其他介绍

01.关于博客汇总链接

02.关于我的博客

开源代码案例:https://github.com/yangchong211/LifeHelper

目录
相关文章
|
3天前
|
机器学习/深度学习 算法
基于蛙跳优化的神经网络数据预测matlab仿真
使用MATLAB2022a,应用蛙跳优化算法(SFLA)调整神经网络权重,提升预测精度,输出预测曲线。神经网络结合输入、隐藏和输出层进行预测,蛙跳算法模仿蛙群觅食行为优化权重和阈值。算法流程包括蛙群初始化、子群划分、局部搜索及适应度更新,直至满足停止条件。优化后的神经网络能提升预测性能。
|
3天前
|
机器学习/深度学习 算法
m基于PSO-GRU粒子群优化长门控循环单元网络的电力负荷数据预测算法matlab仿真
摘要: 在MATLAB 2022a中,对比了电力负荷预测算法优化前后的效果。优化前为&quot;Ttttttt111222&quot;,优化后为&quot;Tttttttt333444&quot;,明显改进体现为&quot;Tttttttttt5555&quot;。该算法结合了粒子群优化(PSO)和长门控循环单元(GRU)网络,利用PSO优化GRU的超参数,提升预测准确性和稳定性。PSO模仿鸟群行为寻找最优解,而GRU通过更新门和重置门处理长期依赖问题。核心MATLAB程序展示了训练和预测过程,包括使用&#39;adam&#39;优化器和超参数调整,最终评估并保存预测结果。
6 0
|
4天前
|
存储 分布式计算 固态存储
Hadoop性能优化硬件和网络优化
【6月更文挑战第7天】
14 3
|
9天前
|
缓存 安全 前端开发
(转)浅谈tomcat优化(内存,并发,缓存,安全,网络,系统等)
(转)浅谈tomcat优化(内存,并发,缓存,安全,网络,系统等)
|
12天前
|
机器学习/深度学习 安全 算法
利用机器学习优化网络安全防御策略
【6月更文挑战第3天】随着网络攻击的日益猖獗,传统的安全防御机制已难以满足企业对数据保护的需求。本文探讨如何应用机器学习技术来预测和防御潜在的网络安全威胁,通过分析历史数据模式,自动调整安全策略,从而在不断变化的威胁环境中保持企业的网络安全。
|
15天前
|
缓存 UED
【计算巢】移动网络优化技巧:提升用户体验的关键
【6月更文挑战第1天】在数字化时代,移动网络已成为生活必需,但网络问题时常影响用户体验。本文探讨了移动网络优化技巧,包括理解网络基本原理、增强信号强度、避免网络拥堵及示例代码演示如何监听和管理网络状态。通过这些方法,可以提升网络效率和稳定性,优化用户体验。
【计算巢】移动网络优化技巧:提升用户体验的关键
|
16天前
|
JSON Android开发 开发者
构建高效Android应用:采用Kotlin协程优化网络请求
【5月更文挑战第31天】 在移动开发领域,尤其是针对Android平台,网络请求的管理和性能优化一直是开发者关注的焦点。随着Kotlin语言的普及,其提供的协程特性为异步编程提供了全新的解决方案。本文将深入探讨如何利用Kotlin协程来优化Android应用中的网络请求,从而提升应用的响应速度和用户体验。我们将通过具体实例分析协程与传统异步处理方式的差异,并展示如何在现有项目中集成协程进行网络请求优化。
|
16天前
|
机器学习/深度学习 网络安全 决策智能
利用深度学习优化图像识别准确性的探索云计算与网络安全的融合:保护云服务的关键策略
【5月更文挑战第30天】 在计算机视觉领域,图像识别的准确性对于后续处理和分析至关重要。本文旨在探讨一种基于深度学习的方法来优化图像识别过程,通过构建一个更加精细和强大的卷积神经网络(CNN)模型,实现对图像特征的深层次挖掘和精确分类。文章首先回顾了当前图像识别中存在的挑战,接着提出一种新型的CNN架构,并通过实验验证了其在多个标准数据集上的性能表现。研究结果表明,该模型在提高识别精度的同时,还能有效降低误识率,为图像识别技术的进步提供了新的视角和解决方案。
|
18天前
|
机器学习/深度学习 安全 网络安全
利用机器学习优化数据中心能效的研究数字堡垒的构建者:网络安全与信息安全的深层探索
【5月更文挑战第29天】在云计算和大数据时代,数据中心的能效问题成为关键挑战之一。本文通过集成机器学习技术与现有数据中心管理策略,提出了一种新型的智能优化框架。该框架能够实时分析数据中心的能耗模式,并自动调整资源分配,以达到降低能耗的目的。研究结果表明,应用机器学习算法可以显著提升数据中心的能源使用效率,同时保持服务质量。
|
18天前
|
人工智能 安全 网络安全
网络安全与信息安全:防护之道探索现代数据中心的能效优化策略
【5月更文挑战第29天】 在数字化时代,网络安全与信息安全已成为我们不可忽视的问题。本文将深入探讨网络安全漏洞的成因,加密技术的应用,以及提升安全意识的重要性。我们将了解到,网络安全并非只是技术问题,更是一种全民参与的过程。 【5月更文挑战第29天】 在数字化转型的浪潮中,数据中心作为信息处理和存储的核心枢纽,其能源效率已成为衡量其可持续性的关键指标。本文将深入探讨现代数据中心实现能效优化的策略与实践,从硬件选择、冷却系统创新、能源管理软件到人工智能辅助决策,揭示如何通过综合手段提升数据中心运行效率,同时减少环境影响。

热门文章

最新文章