okhttp 拦截器那点事

记录日常用经常使用的拦截器的骚操作

  1. 重试拦截器
  2. Token 失效自动刷新,并重新请求
  3. URL 重定向
  4. 请求体数据加密

1. 重试拦截器

OkHttp 自带 retryOnConnectionFailure(true) 方法可以实现重试,但是不支持自定义重试次数.

所以在项目中需要自定义重试拦截器.

public class RetryIntercepter implements Interceptor {

        public int maxRetry; //最大重试次数
        private int retryNum = 0; //假如设置为3次重试的话,则最大可能请求4次(默认1次+3次重试)

        public RetryIntercepter(int maxRetry) {
            this.maxRetry = maxRetry;
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = chain.proceed(request);
            while (!response.isSuccessful() && retryNum < maxRetry) {
                retryNum++;
                response = chain.proceed(request);
            }
            return response;
        }
    }

在设置重试拦截器时, 需要关闭默认的重试方法 retryOnConnectionFailure(false)

2. Token 自动刷新拦截器

  • Token 在 header 中
public class TokenInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response originalResponse = chain.proceed(request);
            if (isTokenExpired(originalResponse)) { //token 失效  1、这里根据自己的业务需求写判断条件
                return handleTokenInvalid(chain, request);
            }
            return originalResponse;
        }

        private boolean isTokenExpired(Response response) {
            if (response.code() == 401) {
                return true;
            }
            return false;
        }


        // 3. 处理token失效问题
        private Response handleTokenInvalid(Chain chain, Request request) throws IOException {
            String token = refreshToken();
            Request newRequest;
            if (!TextUtils.isEmpty(token)) { // 刷新成功,重新签名
                newRequest = request.newBuilder().removeHeader("token").addHeader("token", token).build();
            } else {
                newRequest = request;
            }
            return chain.proceed(newRequest);
        }

        //刷新token
        private String refreshToken() {
            String token = "";// 2. 获取新 token
            return token;
        }
    }
  1. Token 在 Cookie 中
public class TokenInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = chain.proceed(request);
            if (isTokenExpired(response)) {
                if (refreshToken()) {
                    return chain.proceed(request);
                } else {
                    // 刷新 token 失效,回到登录页,或者其它业务.
                }
            }
            return response;
        }

        private boolean isTokenExpired(Response response) {
            if (response.code() == 401) {
                return true;
            }
            return false;
        }

        // 1. 获取新 token
        // 必须使用同步请求
        private boolean refreshToken() {
            // 发送同步请求获取新 token
            return true;
        }

    }

// 使用
OkHttpClient client = new OkHttpClient.Builder()
                    .cookieJar(new CookieJar()) // 根据业务实现的 cookiejar
                    .addInterceptor(new TokenInterceptor())
                    .build();

如果 token 是在 cookie 中的,在设置创建 OkHttpClient 时一定要先设置 cookieJar 在设置 Token 拦截器

这样就无需手动设置 token 即可每次在 cookieJar 中获取想的 token

3. URL 重定向

在 okhttp 中,如果请求重定向是 http->https 或者 https->http 是,有事携带的参数会丢失.需要自己定义重定向拦截器.

//处理重定向的拦截器
    public class RedirectInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            okhttp3.Request request = chain.request();
            Response response = chain.proceed(request);
            int code = response.code();
            if (isFollowRedirects(code)) {
                //获取重定向的地址
                String location = response.headers().get("Location");
                //重新构建请求
                Request newRequest = request.newBuilder().url(location).build();
                response = chain.proceed(newRequest);
            }
            return response;
        }

        // 根据业务自定义重定向条件
        private boolean isFollowRedirects(int code) {
            switch (code) {
                case 300:
                case 301:
                    return true;
                default:
                    return false;
            }
        }
    }

使用

    OkHttpClient client = new OkHttpClient.Builder()
                    .followRedirects(false);  //禁制OkHttp的重定向操作,我们自己处理重定向
                    .addInterceptor(new RedirectInterceptor())
                    .build();

4.加解密拦截器

以 POST 表单请求为例

public class EncryptOrDecryptInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            // 当为 post 表单请求时才进行加密
            if (request.method().equals("POST")) {
               RequestBody body = request.body();
               if (body instanceof FormBody) {
                    FormBody.Builder builder = new FormBody.Builder();
                    FormBody formBody = (FormBody) body;
                    // 为每个参数进行加密
                    for (int i = 0; i < formBody.size(); i++) {
                        builder.add(formBody.name(i), AESUtil.encrypt(formBody.value(i)));
                        request = request.newBuilder()
                                .post(builder.build())
                                .build();
                    }
                }
            }

            // 发送请求
            Response response = chain.proceed(request);

            if (response.isSuccessful()) {
                ResponseBody responseBody = response.body();
                if (responseBody != null) {
                    BufferedSource source = responseBody.source();
                    source.request(Long.MAX_VALUE);
                    Buffer buffer = source.buffer();
                    Charset charset = Charset.forName("UTF-8");
                    MediaType contentType = responseBody.contentType();
                    if (contentType != null) {
                        charset = contentType.charset(charset);
                    }
                    
                    // 1. 获取 response body 
                    String bodyString = buffer.clone().readString(charset);
                    // 2. 将 body 进行解密,需根据项目进行定制
                    String decryptBody = AESUtil.decrypt(bodyString);
                    // 3. 构建新的 response,并将解密后的 response body 返回
                    ResponseBody newResponseBody = ResponseBody.create(contentType, decryptBody);
                    response = response.newBuilder().body(newResponseBody).build();
                }
            }
            return response;
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355