美文网首页
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