Retrofit Interceptor(拦截器) 拦截请求并做相关处理

本文介绍Retrofit拦截器(Interceptor)的使用方法及相关注意事项
首先看一下Interceptor源码:

/**
* Observes, modifies, and potentially short-circuits requests going out and the corresponding
* responses coming back in. Typically interceptors add, remove, or transform headers on the request
* or response.
*/

public interface Interceptor {
    Response intercept(Chain chain) throws IOException;

    interface Chain {
        Request request();

        Response proceed(Request request) throws IOException;

        /**
         * Returns the connection the request will be executed on. This is only available in the chains
         * of network interceptors; for application interceptors this is always null.
         */
        @Nullable Connection connection();
    }
}

先看一下api描述,翻译过来其实就是可以通过拦截器拦截即将发出的请求及对响应结果做相应处理,典型的处理方式是修改header。其实我们可能不仅要处理header,有时也需要添加统一参数,都可以在拦截器内部完成。

看一下Interceptor接口,只有intercept(Chain chain)方法,其返回值是Response,顾名思义,是响应数据,我们要做的也就是重写该方法以达到我们的目的。intercept(Chain chain)方法中有个Chain参数,Chain是Interceptor接口内部中定义的另一个接口,我们暂且不管Retrofit内部是如何实现该接口的(这部分内容将会在新的文章中统一讲解),现在只需要知道调用其request()方法可以拿到Request,调用其proceed(Request request)方法可以得到相应数据即可。

到此为止,Interceptor基本用法已经知晓,下面上示例代码:

public class CommonInterceptor implements Interceptor {

    private static Map<String, String> commonParams;

    public synchronized static void setCommonParam(Map<String, String> commonParams) {
        if (commonParams != null) {
            if (CommonInterceptor.commonParams != null) {
                CommonInterceptor.commonParams.clear();
            } else {
                CommonInterceptor.commonParams = new HashMap<>();
            }
            for (String paramKey : commonParams.keySet()) {
                CommonInterceptor.commonParams.put(paramKey, commonParams.get(paramKey));
            }
        }
    }

    public synchronized static void updateOrInsertCommonParam(@NonNull String paramKey, @NonNull String paramValue) {
        if (commonParams == null) {
            commonParams = new HashMap<>();
        }
        commonParams.put(paramKey, paramValue);
    }

    @Override
    public synchronized Response intercept(Chain chain) throws IOException {
        Request request = rebuildRequest(chain.request());
        Response response = chain.proceed(request);
        // 输出返回结果
        try {
            Charset charset;
            charset = Charset.forName("UTF-8");
            ResponseBody responseBody = response.peekBody(Long.MAX_VALUE);
            Reader jsonReader = new InputStreamReader(responseBody.byteStream(), charset);
            BufferedReader reader = new BufferedReader(jsonReader);
            StringBuilder sbJson = new StringBuilder();
            String line = reader.readLine();
            do {
                sbJson.append(line);
                line = reader.readLine();
            } while (line != null);
            LogUtil.e("response: " + sbJson.toString());
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.e(e.getMessage(), e);
        }
//        saveCookies(response, request.url().toString());
        return response;
    }


    public static byte[] toByteArray(RequestBody body) throws IOException {
        Buffer buffer = new Buffer();
        body.writeTo(buffer);
        InputStream inputStream = buffer.inputStream();
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] bufferWrite = new byte[4096];
        int n;
        while (-1 != (n = inputStream.read(bufferWrite))) {
            output.write(bufferWrite, 0, n);
        }
        return output.toByteArray();
    }

    private Request rebuildRequest(Request request) throws IOException {
        Request newRequest;
        if ("POST".equals(request.method())) {
            newRequest = rebuildPostRequest(request);
        } else if ("GET".equals(request.method())) {
            newRequest = rebuildGetRequest(request);
        } else {
            newRequest = request;
        }
        LogUtil.e("requestUrl: " + newRequest.url().toString());
        return newRequest;
    }

    /**
     * 对post请求添加统一参数
     */
    private Request rebuildPostRequest(Request request) {
//        if (commonParams == null || commonParams.size() == 0) {
//            return request;
//        }
        Map<String, String> signParams = new HashMap<>(); // 假设你的项目需要对参数进行签名
        RequestBody originalRequestBody = request.body();
        assert originalRequestBody != null;
        RequestBody newRequestBody;
        if (originalRequestBody instanceof FormBody) { // 传统表单
            FormBody.Builder builder = new FormBody.Builder();
            FormBody requestBody = (FormBody) request.body();
            int fieldSize = requestBody == null ? 0 : requestBody.size();
            for (int i = 0; i < fieldSize; i++) {
                builder.add(requestBody.name(i), requestBody.value(i));
                signParams.put(requestBody.name(i), requestBody.value(i));
            }
            if (commonParams != null && commonParams.size() > 0) {
                signParams.putAll(commonParams);
                for (String paramKey : commonParams.keySet()) {
                    builder.add(paramKey, commonParams.get(paramKey));
                }
            }
            // ToDo 此处可对参数做签名处理 signParams
            /**
             * String sign = SignUtil.sign(signParams);
             * builder.add("sign", sign);
             */
            newRequestBody = builder.build();
        } else if (originalRequestBody instanceof MultipartBody) { // 文件
            MultipartBody requestBody = (MultipartBody) request.body();
            MultipartBody.Builder multipartBodybuilder = new MultipartBody.Builder();
            if (requestBody != null) {
                for (int i = 0; i < requestBody.size(); i++) {
                    MultipartBody.Part part = requestBody.part(i);
                    multipartBodybuilder.addPart(part);

                    /*
                     上传文件时,请求方法接收的参数类型为RequestBody或MultipartBody.Part参见ApiService文件中uploadFile方法
                     RequestBody作为普通参数载体,封装了普通参数的value; MultipartBody.Part即可作为普通参数载体也可作为文件参数载体
                     当RequestBody作为参数传入时,框架内部仍然会做相关处理,进一步封装成MultipartBody.Part,因此在拦截器内部,
                     拦截的参数都是MultipartBody.Part类型
                     */

                    /*
                     1.若MultipartBody.Part作为文件参数载体传入,则构造MultipartBody.Part实例时,
                     需使用MultipartBody.Part.createFormData(String name, @Nullable String filename, RequestBody body)方法,
                     其中name参数可作为key使用(因为你可能一次上传多个文件,服务端可以此作为区分)且不能为null,
                     body参数封装了包括MimeType在内的文件信息,其实例创建方法为RequestBody.create(final @Nullable MediaType contentType, final File file)
                     MediaType获取方式如下:
                     String fileType = FileUtil.getMimeType(file.getAbsolutePath());
                     MediaType mediaType = MediaType.parse(fileType);

                     2.若MultipartBody.Part作为普通参数载体,建议使用MultipartBody.Part.createFormData(String name, String value)方法创建Part实例
                       name可作为key使用,name不能为null,通过这种方式创建的实例,其RequestBody属性的MediaType为null;当然也可以使用其他方法创建
                     */

                    /*
                      提取非文件参数时,以RequestBody的MediaType为判断依据.
                      此处提取方式简单暴力。默认part实例的RequestBody成员变量的MediaType为null时,part为非文件参数
                      前提是:
                      a.构造RequestBody实例参数时,将MediaType设置为null
                      b.构造MultipartBody.Part实例参数时,推荐使用MultipartBody.Part.createFormData(String name, String value)方法,或使用以下方法
                        b1.MultipartBody.Part.create(RequestBody body)
                        b2.MultipartBody.Part.create(@Nullable Headers headers, RequestBody body)
                        若使用方法b1或b2,则要求

                      备注:
                      您也可根据需求修改RequestBody的MediaType,但尽量保持外部传入参数的MediaType与拦截器内部添加参数的MediaType一致,方便统一处理
                     */

                    MediaType mediaType = part.body().contentType();
                    if (mediaType == null) {
                        String normalParamKey;
                        String normalParamValue;
                        try {
                            normalParamValue = getParamContent(requestBody.part(i).body());
                            Headers headers = part.headers();
                            if (!TextUtils.isEmpty(normalParamValue) && headers != null) {
                                for (String name : headers.names()) {
                                    String headerContent = headers.get(name);
                                    if (!TextUtils.isEmpty(headerContent)) {
                                        String[] normalParamKeyContainer = headerContent.split("name=\"");
                                        if (normalParamKeyContainer.length == 2) {
                                            normalParamKey = normalParamKeyContainer[1].split("\"")[0];
                                            signParams.put(normalParamKey, normalParamValue);
                                            break;
                                        }
                                    }
                                }
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            if (commonParams != null && commonParams.size() > 0) {
                signParams.putAll(commonParams);
                for (String paramKey : commonParams.keySet()) {
                    // 两种方式添加公共参数
                    // method 1
                    multipartBodybuilder.addFormDataPart(paramKey, commonParams.get(paramKey));
                    // method 2
//                    MultipartBody.Part part = MultipartBody.Part.createFormData(paramKey, commonParams.get(paramKey));
//                    multipartBodybuilder.addPart(part);
                }
            }
            // ToDo 此处可对参数做签名处理 signParams
            /**
             * String sign = SignUtil.sign(signParams);
             * multipartBodybuilder.addFormDataPart("sign", sign);
             */
            newRequestBody = multipartBodybuilder.build();
        } else {
            try {
                JSONObject jsonObject;
                if (originalRequestBody.contentLength() == 0) {
                    jsonObject = new JSONObject();
                } else {
                    jsonObject = new JSONObject(getParamContent(originalRequestBody));
                }
                if (commonParams != null && commonParams.size() > 0) {
                    for (String commonParamKey : commonParams.keySet()) {
                        jsonObject.put(commonParamKey, commonParams.get(commonParamKey));
                    }
                }
                // ToDo 此处可对参数做签名处理
                /**
                 * String sign = SignUtil.sign(signParams);
                 * jsonObject.put("sign", sign);
                 */
                newRequestBody = RequestBody.create(originalRequestBody.contentType(), jsonObject.toString());
                LogUtil.e(getParamContent(newRequestBody));

            } catch (Exception e) {
                newRequestBody = originalRequestBody;
                e.printStackTrace();
            }
        }
//        可根据需求添加或修改header,此处制作示意
//       return request.newBuilder()
//                .addHeader("header1", "header1")
//                .addHeader("header2", "header2")
//                .method(request.method(), newRequestBody)
//                .build();
        return request.newBuilder().method(request.method(), newRequestBody).build();
    }

    /**
     * 获取常规post请求参数
     */
    private String getParamContent(RequestBody body) throws IOException {
        Buffer buffer = new Buffer();
        body.writeTo(buffer);
        return buffer.readUtf8();
    }

    /**
     * 对get请求做统一参数处理
     */
    private Request rebuildGetRequest(Request request) {
        if (commonParams == null || commonParams.size() == 0) {
            return request;
        }
        String url = request.url().toString();
        int separatorIndex = url.lastIndexOf("?");
        StringBuilder sb = new StringBuilder(url);
        if (separatorIndex == -1) {
            sb.append("?");
        }
        for (String commonParamKey : commonParams.keySet()) {
            sb.append("&").append(commonParamKey).append("=").append(commonParams.get(commonParamKey));
        }
        Request.Builder requestBuilder = request.newBuilder();
        return requestBuilder.url(sb.toString()).build();
    }
}

该拦截器示例代码提供了插入公共参数及对添加header功能(该功能在代码中被注释掉,如需要,放开即可)。对Request的拦截处理在rebuildRequest(Request request) 方法中完成,该方法只处理了GET与POST请求,内部有较为详尽的注释,较为复杂的是文件传输,有些需要注意的事项也做了尽可能完善的说明;对响应数据的处理,代码示例中只做了结果输出处理,仅仅做个示范。

拦截器部分没有过多需要做说明的地方,比较简单,本文的示例可直接使用。如有疑问,欢迎留言。
后续将抽时间,对Retrofit做流程上的简单梳理,了解各个配置及部分细节实现,比如该文中的Chain实例
完整示例:https://github.com/670832188/TestApp

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 你可以使用OkHttp的Interceptor拦截参数。在使用Retrofit时,可以通过自定义OkHttpClient并添加Interceptor来实现。以下是一个示例: ``` OkHttpClient okHttpClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); Request requestWithNewParameter = originalRequest.newBuilder() .addHeader("Your-Header-Name", "Your-Header-Value") .build(); return chain.proceed(requestWithNewParameter); } }) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build(); YourApiService apiService = retrofit.create(YourApiService.class); ``` 在上面的示例中,我们添加了一个Interceptor拦截每个请求并添加一个自定义的Header。你可以根据需要修改拦截器的实现来拦截并修改请求参数。 ### 回答2: 在Android开发中,Retrofit是一个非常流行的网络请求库。它提供了拦截器Interceptor)的功能,可以对网络请求的参数进行拦截和修改。 拦截器是一个接口,可以在请求之前或者响应之后对请求进行处理。在Retrofit中,可以通过实现Interceptor接口来自定义拦截器。 通过拦截器,我们可以对请求进行一些通用的处理,比如对请求头进行添加、修改、删除等操作。我们可以在拦截器的intercept方法中获取到请求的参数,然后根据需求进行修改。 拦截器可以被添加到OkHttpClient的拦截器链中,通过addInterceptor()方法来添加。当发起网络请求时,拦截器会按照添加的顺序进行拦截。这样,我们就可以在请求中添加或修改参数。 拦截器还可以对响应进行处理,比如对返回的数据进行解析、转换等操作。拦截器可以获取到响应的数据,然后可以对数据进行一些处理,并返回给调用方。 通过使用拦截器,我们可以在不修改原有的网络请求代码的情况下,对请求和响应进行一些通用的处理。这样可以提高开发的效率,并且可以保持代码的整洁和可维护性。 总的来说,Android Retrofit拦截器功能非常强大,可以非常方便地对网络请求的参数进行拦截和修改,提供了非常灵活的扩展性。 ### 回答3: 在Android中,Retrofit是一个广泛使用的网络请求库,用于方便地从服务器获取数据。拦截参数是指在使用Retrofit发送网络请求之前,可以通过拦截器请求的参数进行修改或添加一些额外的信息。 在Retrofit中,可以通过自定义拦截器来实现对参数的拦截操作。拦截器是一个接口,它提供了两个方法:intercept和proceed。 intercept方法用于处理拦截逻辑,我们可以在这个方法中获取到请求的参数信息,并对其进行修改或添加额外信息。可以使用request.body()方法获取请求体,再通过request.newBuilder()方法创建一个新的请求,最后通过chain.proceed()方法继续请求的发送。 proceed方法表示继续发送原始请求,如果不调用该方法,请求将会被拦截,不会发送到服务器。 要使用拦截器,首先需要创建一个OkHttpClient对象,并通过addInterceptor方法将拦截器添加到OkHttpClient中。然后将创建的OkHttpClient对象传递给Retrofit.Builder的client方法。 以下是一个示例代码,演示如何使用拦截器请求参数进行拦截: ``` OkHttpClient.Builder builder = new OkHttpClient.Builder(); builder.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // 获取原始请求的参数 HttpUrl url = request.url().newBuilder() .addQueryParameter("key", "value") .build(); Request newRequest = request.newBuilder() .url(url) .build(); return chain.proceed(newRequest); } }); OkHttpClient client = builder.build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://example.com/") .client(client) .build(); ``` 上述代码创建了一个OkHttpClient对象,并添加了一个拦截器。在拦截器的intercept方法中,获取了原始请求的参数,然后对其进行了修改(添加了一个名为key值为value的参数),最后调用chain.proceed方法继续发送请求。 通过拦截器,我们可以对请求的参数进行灵活处理,例如对请求进行签名、添加公共参数等操作,提高了开发的灵活性和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值