android OkHttp拦截器(Interceptor)的使用

一 概念

    在OkHttp内部是使用拦截器来完成请求和响应,利用的是责任链设计模式,可以用来转换,重试,重写请求的机制。现在主流的网络框架非Retrofit莫属,它的内部请求也是基于OkHttp的。

     在每一个拦截器中,一个关键部分就是使用chain.proceed(request)发起请求。这个简单的方法就是所有Http工作发生的地方,生成和请求对应的响应。

    多个拦截器可以链接使用。假设一个压缩拦截器和一个校验和拦截器:需要决定数据是否先被压缩,然后校验或者先校然后再压缩。OkHttp使用列表来跟踪拦截器,并按顺序调用拦截器。

如上图所示,就是OkHttp中数据流的传输方向,里面包含了两种拦截器

  • Application Interceptors应用程序拦截器
  • Network Interceptors网络拦截器。

二 分类

1.应用拦截器

通过下面两种方式注册的为应用拦截器:


//方式一:在OkHttpClient.Builder中添加
new OkHttpClient.Builder().addInterceptor(interceptor)


//方式二:在okHttpClient中直接添加
okHttpClient.interceptors().add(interceptor)

         主要用于查看请求信息及返回信息,如链接地址、头信息、参数信息等,如下面的log-拦截器定义:

class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();

    long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s", request.url(), 
chain.connection(), request.headers()));

    Response response = chain.proceed(request);
    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));

    return response;
  }
}

2.网络拦截器

通过下面两种方式注册的为网络拦截器:

//方式一:在OkHttpClient.Builder中添加
new OkHttpClient.Builder().addNetworkInterceptor(interceptor)

//方式二:在okHttpClient中直接添加
okHttpClient. networkInterceptors().add(interceptor)

       可以添加、删除或替换请求头信息,还可以改变的请求携带的实体。


   1)添加请求头,假设后台要求我们请求API接口时,要在每一个接口请求头上添加对应的Token。使用Retrofit的话可能条件反射出以下代码:

@FormUrlEncoded
@POST("/mobile/login.htm")
Call<ResponseBody> login(@Header("token") String token, @Field("mobile") String phoneNumber, @Field("smsCode") String smsCode);

这样写是可以,但是如果接口很多的话每一个都需要传入token,重复很多遍,很容易导致代码的冗余。
    下面使用拦截器就可以一劳永逸了,代码如下:
public class TokenHeaderInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        // get token
        String token = AppService.getToken();
        Request originalRequest = chain.request();
        // get new request, add request header
        Request updateRequest = originalRequest.newBuilder()
                .header("token", token)
                .build();
        return chain.proceed(updateRequest);
    }

    先拦截得到originalRequest,然后利用originalRequest生成新的updateRequest,再交给chain处理进行下一环。最后在OkhttpClient中使用:
OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(new TokenHeaderInterceptor())
                .build();

Retrofit retrofit = new Retrofit.Builder().baseUrl(BuildConfig.BASE_URL)
                .client(client).addConverterFactory(GsonConverterFactory.create()).build();

2)修改请求体
假设有以下需求:在上面login接口基础上,后台要求我们传过去的请求参数要按照一定规则经过加密的。


规则如下:
  • 请求参数名统一为content;
  • content值:JSON 格式的字符串经过 AES 加密后的内容;
举个例子,根据上面login接口,现有
{"mobile":"157xxxxxxxx", "smsCode":"xxxxxx"}
Json字符串,然后再将其加密。最后以content = [加密后json字符串]方式发送给后台。

看完了上面的  TokenHeaderInterceptor  之后,这需求对于我们来说可以算是信手拈来:

 
public class RequestEncryptInterceptor implements Interceptor {
    private static final String FORM_NAME = "content";
    private static final String CHARSET = "UTF-8";

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        //获取请求体 
        RequestBody body = request.body();
        if (body instanceof FormBody) {
            FormBody formBody = (FormBody) body;
            Map<String, String> formMap = new HashMap<>();
            // 从 formBody 中拿到请求参数,放入 formMap 中 
            for (int i = 0; i < formBody.size(); i++) {
                formMap.put(formBody.name(i), formBody.value(i));
            }
            // 将 formMap通过Gson.toJson() 转化为 json 然后 AES 加密 
            Gson gson = new Gson();
            String jsonParams = gson.toJson(formMap);
            String encryptParams = AESCryptUtils.encrypt(jsonParams.getBytes(CHARSET), AppConstant.getAESKey());
            // 重新修改 body 的内容 
            body = new FormBody.Builder().add(FORM_NAME, encryptParams).build();
        }
        // 若请求体不为Null,重新构建post请求,并传入修改后的参数体 
        if (body != null) {
            request = request.newBuilder().post(body).build();
        }
        return chain.proceed(request);
    }
}


三 选择

    每个拦截器都有各自的优点

Application interceptors应用程序拦截器

  • 不需要担心比如重定向和重试的中间响应。
  • 总是被调用一次,即使HTTP响应结果是从缓存中获取的。
  • 监控应用程序的原始意图。不关心例如OkHttp注入的头部字段If-None-Match。
  • 允许短路,不调用Chain.proceed()。
  • 允许重试并多次调用Chain.proceed()。

Network Interceptors网络拦截器

  • 能够对中间的响应进行操作比如重定向和重试。
  • 当发生网络短路时,不调用缓存的响应结果。
  • 监控数据,就像数据再网络上传输一样。
  • 访问承载请求的连接Connection。
  • 10
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OkHttp是一个用于处理HTTP请求的开源Java库。它提供了一个拦截机制,可以在发送请求和接收响应之前对它们进行修改和处理。以下是关于OkHttp拦截的一些介绍和示例: 1. OkHttp拦截是一个接口,它有一个方法intercept(Chain chain),该方法接收一个Chain对象作为参数,该对象表示当前的拦截链。 2. 拦截链是按照添加顺序执行的,每个拦截都可以选择将请求传递给下一个拦截或者直接返回响应。 3. 拦截可以在请求和响应中添加、修改或删除头信息,也可以重试请求或者记录请求和响应的日志等。 以下是一个简单的OkHttp拦截示例,它会在请求头中添加一个自定义的User-Agent信息: ```java public class UserAgentInterceptor implements Interceptor { private static final String USER_AGENT_HEADER = "User-Agent"; private final String userAgent; public UserAgentInterceptor(String userAgent) { this.userAgent = userAgent; } @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Request newRequest = request.newBuilder() .header(USER_AGENT_HEADER, userAgent) .build(); return chain.proceed(newRequest); } } ``` 在上面的示例中,我们创建了一个名为UserAgentInterceptor拦截,它接收一个User-Agent字符串作为参数。在intercept方法中,我们首先获取当前的请求对象,然后使用Request.Builder添加一个自定义的User-Agent头信息,最后使用chain.proceed方法将请求传递给下一个拦截或者返回响应。 以下是一个使用上面定义的拦截的示例: ```java OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new UserAgentInterceptor("MyApp/1.0")) .build(); ``` 在上面的示例中,我们创建了一个OkHttpClient对象,并使用addInterceptor方法添加了一个UserAgentInterceptor拦截。这样,在发送请求时,OkHttp会自动调用我们定义的拦截,并在请求头中添加一个自定义的User-Agent信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值