技术经验谈 技术经验谈
首页
  • 最佳实践

    • 抓包
    • 数据库操作
  • ui

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • 总纲
  • 整体开发框架
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

hss01248

一号线程序员
首页
  • 最佳实践

    • 抓包
    • 数据库操作
  • ui

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • 总纲
  • 整体开发框架
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 最佳实践

  • ui

  • 优化

  • aop

  • apm

  • 架构

    • Android布局方案
    • android技术选型
    • okhttp拦截器的一些常规操作
    • okhttp日志-监测-可视化
    • reactnative崩溃处理
    • 一些全局处理
    • 关于网络框架设计封装的扯淡
    • 日志回捞系统设计
    • 设备唯一性id问题
  • webview

  • rxjava

  • activity-fragment-view的回调和日志
  • Android加密相关
  • Android命令行操作
  • app后台任务
  • kotlin
  • kotlin漫谈
  • kotlin语言导论
  • sentry上传mapping.txt文件
  • so放于远程动态加载方案
  • states
  • Xposed模块开发
  • 一个关于manifest合并的猥琐操作
  • 玩坏android存储
  • 获取本app的安装来源信息
  • Android
  • 架构
hss01248
2021-05-28

okhttp拦截器的一些常规操作

# Okhttp拦截器的一些常规操作

# 日志和各种ui查看信息:

  • Chucker (opens new window): An in-app HTTP inspector for Android OkHttp clients.
  • Flipper (opens new window): A desktop debugging platform for mobile developers.
  • OkLog (opens new window): Response logging interceptor for OkHttp. Logs a URL link with URL-encoded response for every OkHttp call.

# 添加公共请求头

不建议初始化网络库时放到一个map,然后每次读取

比较建议直接在拦截器里每次赋值

 @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
       
        Request.Builder builder = original.newBuilder();
        builder.addHeader(VERSION_CODE, code);
        builder.addHeader(DEVICE_TYPE, type);
        return chain.proceed(builder.build());
    }
1
2
3
4
5
6
7
8
9

# 对请求体进行gzip压缩

Request compressedRequest = originalRequest.newBuilder()
        .header("Content-Encoding", "gzip")
        .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body())))
        .build();
return chain.proceed(compressedRequest);


 private RequestBody gzip(final RequestBody body) {
        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return body.contentType();
            }

            @Override
            public long contentLength() {
                return -1; // We don't know the compressed length in advance!
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
              //gzip压缩
                BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                body.writeTo(gzipSink);
                gzipSink.close();
            }
        };
    }
//获取gzip后的大小
private RequestBody forceContentLength(final RequestBody requestBody) throws IOException {
        final Buffer buffer = new Buffer();
        requestBody.writeTo(buffer);

        final long size = buffer.size();

        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return requestBody.contentType();
            }

            @Override
            public long contentLength() {
                return size;
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                sink.write(buffer.snapshot());
               
            }
        };
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 对请求体进行加密

下面的几行也是获取requestBody里字节流的通用方法

 private RequestBody forceContentLength(final RequestBody requestBody) throws IOException {
      
   final Buffer buffer = new Buffer();
        requestBody.writeTo(buffer);

        final long size = buffer.size();


        final byte[] bytes = new byte[(int) size];
        buffer.readFully(bytes);

        final byte[] bytesEncrypted = encrypt(bytes);
        if(DeviceReporter.getConfig().isDebug()){
            Log.w("buffer","buffer.readFully(bytes)\n"+new String(bytes));
            Log.w("buffer","after encrypted\n"+new String(bytesEncrypted));

            Log.w("buffer","after decrypted\n"+new String(decrypt(bytesEncrypted)));
        }


        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return requestBody.contentType();
            }

            @Override
            public long contentLength() {
                return size;
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                sink.write(bytesEncrypted);
            }
        };
    }

private static final String AES = "AES";
private static final String CRYPT_KEY ="16位或32位长度字符串"
private Key getSecretKey() {
        return new SecretKeySpec(CRYPT_KEY.getBytes(), AES);
    }

    private byte[] encrypt(byte[] bytes) {
        try {
            InputStream is = new ByteArrayInputStream(bytes);//目标
            ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length);
            //SecretKey deskey = new SecretKeySpec(getKey().getBytes(), ENCRYPT_TYPE);
            //Key length not 128/192/256 bits
            Cipher cipher = Cipher.getInstance(AES);
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey());
            // 创建加密流
            CipherInputStream cis = new CipherInputStream(is, cipher);
            byte[] buffer = new byte[1024];
            int r;
            while ((r = cis.read(buffer)) > 0) {
                out.write(buffer, 0, r);
            }
            return out.toByteArray();
        }catch (Throwable throwable){
            XLogUtil.exception("aes",throwable);
            return bytes;
        }
    }

    private byte[] decrypt(byte[] bytes) {
        try {
            InputStream is = new ByteArrayInputStream(bytes);//目标
            ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length);
            //SecretKey deskey = new SecretKeySpec(getKey().getBytes(), ENCRYPT_TYPE);
            //Key length not 128/192/256 bits
            Cipher cipher = Cipher.getInstance(AES);
            cipher.init(Cipher.DECRYPT_MODE, getSecretKey());
            // 创建加密流
            CipherInputStream cis = new CipherInputStream(is, cipher);
            byte[] buffer = new byte[1024];
            int r;
            while ((r = cis.read(buffer)) > 0) {
                out.write(buffer, 0, r);
            }
            return out.toByteArray();
        }catch (Throwable throwable){
            throwable.printStackTrace();
            return bytes;
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

# 文件上传时获取文件路径:

//拿到RequestBody源码里静态方法的参数file:

public static RequestBody create(final @Nullable MediaType contentType, final File file) {
    if (file == null) throw new NullPointerException("file == null");

    return new RequestBody() {
      @Override public @Nullable MediaType contentType() {
        return contentType;
      }

      @Override public long contentLength() {
        return file.length();
      }

      @Override public void writeTo(BufferedSink sink) throws IOException {
        Source source = null;
        try {
          source = Okio.source(file);
          sink.writeAll(source);
        } finally {
          Util.closeQuietly(source);
        }
      }
    };
  }
//通过下面的反射方法可以拿到:


private String getFilePath(RequestBody requestBody) {
        try {
            //ContentTypeOverridingRequestBody
            //RequestBody$3
            Class clazz = requestBody.getClass();
            String name = clazz.getName();
            if(name.contains("ContentTypeOverridingRequestBody")){
                Field field = clazz.getDeclaredField("delegate");
                field.setAccessible(true);
                requestBody = (RequestBody) field.get(requestBody);

                clazz = requestBody.getClass();
            }

            Field field = clazz.getDeclaredField("val$file");
            field.setAccessible(true);
            File file = (File) field.get(requestBody);
            return file.getAbsolutePath();


        }catch (Throwable throwable){
            throwable.printStackTrace();
        }
        return "";
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 拦截重复请求(慎用)

一段时间内(比如30s)缓存response对象,判定请求是重复发起时,等待响应,如果后续响应成功,就返回"请求重复"的响应.否则继续发送.

关键在如何判断两个请求是否重复发送: url,时间间隔,请求参数,请求体,cookie

# 将cookie抛到应用拦截器层

默认情况下,普通应用拦截器无法拿到cookie信息,在下层被okhttp内置的拦截器透明处理了,如何在应用拦截器里也拿到cookie信息呢?

创建一个网络拦截器,在拦截器层保存请求和响应的cookie信息,用另外的header字段,回写到响应里:

public class PrintCookieNetworkInterceptor implements Interceptor {



    public static final String KEY_REQUEST_COOKIE = "log-request-cookie";
    public static final String KEY_REQUEST_CONTENT_LENGTH = "log-request-Content-Length";
    public static final String KEY_REQUEST_CONTENT_TYPE = "log-request-Content-Type";
    public static final String KEY_REQUEST_ACCEPT_ENCODING = "log-request-Accet-Encoding";

    public static final String KEY_SET_COOKIE = "log-response-cookie";
    public static final String KEY_RESPONSE_CONTENT_LENGTH = "log-response-Content-Length";
    public static final String KEY_RESPONSE_CONTENT_ENCODING = "log-response-Content-Encoding";
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();

        String requestCookie = request.header("Cookie");
        if(requestCookie == null){
            requestCookie = "";
        }
        String requestContentLength = request.header("Content-Length");
        if(requestContentLength == null){
            requestContentLength = "";
        }
        String requestContentType = request.header("Content-Type");
        if(requestContentType == null){
            requestContentType = "";
        }
        String requestAcceptEncoding = request.header("Accept-Encoding");
        if(requestAcceptEncoding == null){
            requestAcceptEncoding = "";
        }


       Response response =  chain.proceed(request);

        if(!ChuckInterceptor.isEnable()){
            return response;
        }


        List<String> cookieStrings = response.headers().values("Set-Cookie");
        String responseCookie = "";

        if(cookieStrings!= null && !cookieStrings.isEmpty()){
            responseCookie = Arrays.toString(cookieStrings.toArray());
        }
        String length = response.header("Content-Length");
        if(length == null){
            length = "";
        }
        String encoding = response.header("Content-Encoding");
        if(encoding == null){
            encoding = "";
        }


        response = response.newBuilder()
                .header(KEY_REQUEST_ACCEPT_ENCODING,requestAcceptEncoding)
                .header(KEY_REQUEST_CONTENT_LENGTH,requestContentLength)
                .header(KEY_REQUEST_CONTENT_TYPE,requestContentType)
                .header(KEY_REQUEST_COOKIE,requestCookie)
                .header(KEY_SET_COOKIE,responseCookie)
                .header(KEY_RESPONSE_CONTENT_LENGTH,length)
                .header(KEY_RESPONSE_CONTENT_ENCODING,encoding)
                .build();
        return response;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
编辑 (opens new window)
上次更新: 2022/08/25, 20:20:31
android技术选型
okhttp日志-监测-可视化

← android技术选型 okhttp日志-监测-可视化→

最近更新
01
截图后的自动压缩工具
12-27
02
图片视频文件根据exif批量重命名
12-27
03
chatgpt图片识别描述功能
02-20
更多文章>
Theme by Vdoing | Copyright © 2020-2025 | 粤ICP备20041795号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式