Retrofit2自定义的日志拦截器interceptor

本文介绍如何在Retrofit2中实现自定义日志拦截器,兼容GET和POST请求,展示请求参数及响应内容,并解析JSON格式数据。
  • 通过Retrofit2 拦截器获取到请求参数和响应内容
  • 兼容了POST和GET请求
  • 返回值以JSON格式展示

1、Retrofit2中使用自定义的日志拦截器:

@Override
public void okHttpClient(OkHttpClient.Builder okHttpClient) {
    okHttpClient.connectTimeout(5000, TimeUnit.MILLISECONDS)
            .writeTimeout(60000, TimeUnit.MILLISECONDS)
            .readTimeout(60000, TimeUnit.MILLISECONDS)
            .addInterceptor(chain -> {
                Request.Builder builder = chain.request().newBuilder();
                AccountBean bean = MainApplication.getInstance().getAccountBean();
                builder.addHeader("user_id", bean.getUserCardNum());//身份证号码

                return chain.proceed(builder.build());
            });

    okHttpClient.addInterceptor(new HttpLogReportInterceptor());//统一日志拦截器
}

2、HttpLogReportInterceptor.java 源码:


import android.net.Uri;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;

public class HttpLogReportInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        int httpCode = response.code();//获取响应码
        String url = request.url().toString();
        String requestParam = "";

        switch (request.method().toLowerCase()) {
            case "post":
                requestParam = getRequestParams4Post(request);//请求参数
                break;
            case "get":
                requestParam = getRequestParams4Get(request.url().toString());
                break;
        }

        String jsonStr = getResponseContent(response);
        int code = -1;//业务逻辑的code

        // 1、接口调用成功(有响应)
        // 2、业务逻辑执行完成
        try {
            HttpBaseResult baseResult = new Gson().fromJson(jsonStr, HttpBaseResult.class);
            code = baseResult.getStatus();
//            LegoLog.d("业务逻辑 Code:" + code);
        } catch (Exception e) {
            LegoLog.w(e.getMessage());
        }
        if (httpCode == 200 && (code == 200)) {
            // 1、接口调用成功(有响应)
            // 2、业务逻辑执行完成

        } else {
            LegoLog.d("Http Code:" + httpCode + ";;业务code:" + code);
        }

        return response;
    }

    private String getRequestParams4Get(String url) {
        Uri uri = Uri.parse(url);
        JsonObject requestJson = new JsonObject();
        for (String paramKey : uri.getQueryParameterNames()) {
            String paramValue = uri.getQueryParameter(paramKey);
            requestJson.addProperty(paramKey, paramValue);
        }
        return requestJson.toString();
    }
    
    /**
     * 获取POST请求参数
     */
    private String getRequestParams4Post(Request request) throws IOException {
        if (request.body() != null && HttpContentTypeUtil.isParseable(request.body().contentType())) {
            return parseRequestParams4Post(request);
        }
        // 说明:multipart/form-data 需要特殊处理
        return "";
    }

    /**
     * 解析请求服务器的请求参数
     */
    private static String parseRequestParams4Post(Request request) throws UnsupportedEncodingException {
        try {
            RequestBody body = request.body();
            if (body == null) return "";
            Buffer requestBuffer = new Buffer();
            body.writeTo(requestBuffer);
            Charset charset = StandardCharsets.UTF_8;
            MediaType contentType = body.contentType();

            if (contentType != null) {
                charset = contentType.charset(charset);
            }
            String text = requestBuffer.readString(charset);

            if (contentType != null && !"json".equals(contentType.subtype())) {
                text = URLDecoder.decode(text, convertCharset(charset));
            }

            return TextUtil.jsonFormat(text);
        } catch (IOException e) {
            e.printStackTrace();
            return "{\"error\": \"" + e.getMessage() + "\"}";
        }
    }

    private static String convertCharset(Charset charset) {
        String s = charset.toString();
        int i = s.indexOf("[");
        if (i == -1)
            return s;
        return s.substring(i + 1, s.length() - 1);
    }
    
    /**
     * 获取返回值内容
     *
     * @param response
     * @return
     */
    private String getResponseContent(Response response) {
        try {
            //读取服务器返回的结果
//            ResponseBody responseBody = response.newBuilder().build().body();
            ResponseBody responseBody = response.body();
            BufferedSource source = responseBody.source();
            source.request(Long.MAX_VALUE); // Buffer the entire body.
            Buffer buffer = source.buffer();

            //获取content的压缩类型
            String encoding = response.headers().get("Content-Encoding");
            //克隆服务器的响应内容()
            Buffer cloneBuffer = buffer.clone();

            //解析response content
            return parseResponseContent(responseBody.contentType(), encoding, cloneBuffer);
        } catch (IOException e) {
            LegoLog.w(e.getMessage());
            return "{\"error\": \"" + e.getMessage() + "\"}";
        }
    }

    /**
     * 解析服务器响应的内容
     *
     * @param contentType
     * @param encoding    编码类型
     * @param cloneBuffer 克隆后的服务器响应内容
     * @return 解析后的响应结果
     */
    private String parseResponseContent(MediaType contentType, String encoding, Buffer cloneBuffer) {
        Charset charset = StandardCharsets.UTF_8;
        if (contentType != null) {
            charset = contentType.charset(charset);
        }
        if (encoding != null && encoding.equalsIgnoreCase("gzip")) {//content使用gzip压缩
            return ZipUtil.decompressForGzip(cloneBuffer.readByteArray(), convertCharset(charset));//解压
        } else if (encoding != null && encoding.equalsIgnoreCase("zlib")) {//content使用zlib压缩
            return ZipUtil.decompressToStringForZlib(cloneBuffer.readByteArray(), convertCharset(charset));//解压
        } else {//content没有被压缩
            return cloneBuffer.readString(charset);
        }
    }
}

HttpBaseResult.java


public class HttpBaseResult<T> {
    private int code;
    private String msg;
    private T data;
	
	//...get/set
}

特别说明:

ResponseBody body = response.body();//获取响应体
// 由于ResponseBody内的bytes()、string()方法调用了closeQuietly方法,此方法会将ResponseBody内的数据源清除,所以仅获取一次内容。
byte[] bytes = body.bytes();//获取字节数组

由于ResponseBody内的 bytes()、string()方法调用了== Util.closeQuietly(source);==,此方法会将ResponseBody内的数据源清除,所以仅获取一次内容。

OkHttp请求回调中response.body().string()只能有效调用一次

<think>好的,我现在需要解决用户关于Retrofit自定义拦截器不生效的问题。首先,用户提到可能是配置问题或使用方式不正确,我需要从这些方面入手。 首先,我应该回顾一下Retrofit和OkHttp的基本结构。Retrofit本身依赖于OkHttp来处理网络请求,拦截器是OkHttp的一部分。所以,用户的问题可能出在如何正确地将拦截器添加到OkHttpClient中,然后再将OkHttpClient配置到Retrofit实例里。 接下来,用户可能没有正确添加拦截器。比如,可能用了addInterceptor()方法,但应该确认是否在构建OkHttpClient时正确调用了这个方法。另外,需要区分网络拦截器和应用拦截器,应用拦截器使用addInterceptor(),而网络拦截器使用addNetworkInterceptor(),两者用途不同,如果加错了地方可能导致拦截器不生效。 然后,检查拦截器的顺序。如果同时有其他拦截器,比如日志拦截器,可能自定义拦截器被放在后面,导致某些情况下被跳过。或者,如果拦截器链中有某个拦截器提前终止了请求,比如缓存拦截器返回了缓存的响应,那么后面的拦截器就不会被执行了。 另外,用户可能没有正确创建新的OkHttpClient实例。如果复用了一个已经配置好的实例,而没有添加自定义拦截器,自然会导致拦截器不生效。这时候需要确认在构建Retrofit时是否传入了正确的OkHttpClient实例。 还有可能是ProGuard或混淆的问题,导致拦截器类被移除或重命名,但这种情况可能较少见,用户可能是在发布版本遇到,但如果是开发阶段,这可能不是主要原因。 此外,拦截器内部是否有错误导致其被跳过?比如,拦截器中抛出了异常,或者在处理过程中没有正确调用chain.proceed(request),导致请求没有继续传递,这样拦截器虽然执行了,但可能没有达到预期效果,甚至导致请求失败。 还需要检查是否在正确的OkHttpClient上添加了拦截器。比如,如果用户有多个OkHttpClient实例,可能错误地将拦截器添加到了未被Retrofit使用的那个实例上。 最后,考虑日志拦截器的影响,比如用户可能同时使用了HttpLoggingInterceptor,并且设置为BODY级别,这可能会影响文件下载或其他类型的响应,但不确定是否与拦截器不执行有关。不过根据引用[3],BODY级别的日志拦截器可能会影响流式下载,但这里的问题可能更基础,比如拦截器根本没被调用。 总结可能的原因: 1. 拦截器未正确添加到OkHttpClient。 2. OkHttpClient未正确配置到Retrofit实例。 3. 拦截器顺序或类型(应用/网络)错误。 4. 其他拦截器导致提前终止。 5. 多个OkHttpClient实例导致混淆。 6. 拦截器内部逻辑错误,未调用proceed。 解决方法需要逐一排查这些可能性,确保正确添加和配置拦截器,检查拦截器顺序,处理多个实例的情况,并验证拦截器内部逻辑是否正确。</think>### Retrofit自定义拦截器不生效的原因及解决方法 #### 1. 拦截器未正确添加到OkHttpClient - **原因**:未通过`addInterceptor()`或`addNetworkInterceptor()`方法添加拦截器,或未将修改后的`OkHttpClient`实例绑定到Retrofit。 - **解决**: ```kotlin val okHttpClient = OkHttpClient.Builder() .addInterceptor(CustomInterceptor()) // 添加自定义拦截器 .build() val retrofit = Retrofit.Builder() .client(okHttpClient) // 绑定到Retrofit .build() ``` #### 2. 拦截器类型混淆(应用拦截器 vs 网络拦截器) - **原因**:应用拦截器(`addInterceptor()`)在请求发送前处理,网络拦截器(`addNetworkInterceptor()`)在建立连接后处理。若需修改请求头等操作,应使用应用拦截器[^2]。 - **解决**: ```kotlin // 正确选择拦截器类型 .addInterceptor(CustomHeaderInterceptor()) // 修改请求头 .addNetworkInterceptor(LoggingInterceptor()) // 记录网络层日志 ``` #### 3. 拦截器顺序导致逻辑冲突 - **原因**:若多个拦截器存在依赖关系,顺序错误可能导致自定义拦截器被跳过。例如,日志拦截器未正确处理响应体时,可能提前终止链式调用[^3][^5]。 - **解决**:调整拦截器顺序,确保自定义拦截器优先执行: ```kotlin .addInterceptor(CustomInterceptor()) .addInterceptor(LoggingInterceptor()) // 日志拦截器放在后面 ``` #### 4. 多个OkHttpClient实例干扰 - **原因**:项目中存在多个`OkHttpClient`实例,Retrofit未使用包含自定义拦截器的实例。 - **解决**:确保Retrofit使用唯一且正确配置的`OkHttpClient`: ```kotlin // 全局统一OkHttpClient配置 object HttpClient { val instance: OkHttpClient by lazy { OkHttpClient.Builder() .addInterceptor(CustomInterceptor()) .build() } } ``` #### 5. 拦截器内部逻辑错误 - **原因**:未调用`chain.proceed(request)`导致请求未继续传递,或未正确处理异常。 - **解决**:检查拦截器实现,确保链式调用完整: ```kotlin class CustomInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request().newBuilder().addHeader("key", "value").build() return chain.proceed(request) // 必须调用proceed } } ``` #### 6. 日志拦截器干扰(特定场景) - **原因**:`HttpLoggingInterceptor`设置为`Level.BODY`时,可能破坏流式下载或文件操作[^3][^4]。 - **解决**:通过条件判断控制日志拦截器的使用: ```kotlin if (BuildConfig.DEBUG) { client.addInterceptor(HttpLoggingInterceptor().setLevel(Level.BASIC)) } ``` #### 验证步骤 1. **添加日志输出**:在自定义拦截器中添加日志语句,确认是否触发。 2. **单元测试**:编写独立测试用例验证拦截器逻辑。 3. **断点调试**:在拦截器的`intercept`方法设置断点,观察是否执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐诺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值