美文网首页
okhttp 拦截器那点事

okhttp 拦截器那点事

作者: yuGodddddd | 来源:发表于2019-12-19 14:27 被阅读0次

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

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

相关文章

网友评论

      本文标题:okhttp 拦截器那点事

      本文链接:https://www.haomeiwen.com/subject/hfrmnctx.html