1. 概述
在开发 Web 应用时,我们经常需要对 HTTP 请求和响应的生命周期进行干预,比如添加一些前置或后置处理逻辑。OkHttp 是一个高效、支持 HTTP/2 的 Android 和 Java 客户端库,它提供了强大的拦截器(Interceptor)机制来实现这一点。
在本文中,我们将深入探讨如何使用 OkHttp 的拦截器来处理请求与响应。
2. 拦截器简介
拦截器是一种可插拔的组件,允许我们在请求被发送之前或响应返回之后对其进行拦截和处理。这种机制非常适合以下场景:
✅ 添加请求头(如认证信息)
✅ 修改请求体或响应体
✅ 日志记录
✅ 错误统一处理
拦截器的核心优势在于 逻辑集中化 和 复用性高,避免了在每个接口中重复编写相同的逻辑。
3. 典型应用场景
拦截器常见用途包括:
- 请求参数日志打印
- 添加认证头(Authorization)
- 响应体压缩
- 响应头增强(如添加 Cookie)
- 错误统一包装
这些功能都可以通过拦截器优雅地实现。
4. 依赖配置
首先,添加 OkHttp 核心依赖:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>5.0.0-alpha.12</version>
</dependency>
测试时可以使用 mockwebserver
来模拟服务端行为:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>5.0.0-alpha.12</version>
<scope>test</scope>
</dependency>
5. 自定义日志拦截器
我们先来实现一个简单的日志拦截器,打印请求头和 URL:
public class SimpleLoggingInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleLoggingInterceptor.class);
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
LOGGER.info("Intercepted headers: {} from URL: {}", request.headers(), request.url());
return chain.proceed(request);
}
}
⚠️ 注意:每个拦截器必须调用 chain.proceed(request)
,否则请求不会继续执行。
5.1. 注册拦截器
将拦截器注册到 OkHttpClient 中:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SimpleLoggingInterceptor())
.build();
✅ 可以多次调用 addInterceptor
添加多个拦截器,它们会按添加顺序执行。
5.2. 编写测试用例
使用 MockWebServer
进行测试:
@Rule
public MockWebServer server = new MockWebServer();
@Test
public void givenSimpleLogginInterceptor_whenRequestSent_thenHeadersLogged() throws IOException {
server.enqueue(new MockResponse().setBody("Hello Baeldung Readers!"));
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SimpleLoggingInterceptor())
.build();
Request request = new Request.Builder()
.url(server.url("/greeting"))
.header("User-Agent", "A Baeldung Reader")
.build();
try (Response response = client.newCall(request).execute()) {
assertEquals("Response code should be: ", 200, response.code());
assertEquals("Body should be: ", "Hello Baeldung Readers!", response.body().string());
}
}
5.3. 测试输出示例
运行测试后,控制台会输出类似日志:
16:07:02.644 [main] INFO c.b.o.i.SimpleLoggingInterceptor - Intercepted headers: User-Agent: A Baeldung Reader
from URL: http://localhost:54769/greeting
5.4. 使用内置的日志拦截器
OkHttp 提供了官方的日志拦截器,可以直接使用:
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>5.0.0-alpha.12</version>
</dependency>
使用方式如下:
HttpLoggingInterceptor logger = new HttpLoggingInterceptor();
logger.setLevel(HttpLoggingInterceptor.Level.HEADERS);
6. 自定义响应头拦截器
下面这个拦截器用于修改响应头,比如设置 Cache-Control
:
public class CacheControlResponeInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
return response.newBuilder()
.header("Cache-Control", "no-store")
.build();
}
}
7. 使用拦截器统一处理错误
我们可以用拦截器来统一包装错误响应,返回 JSON 格式的错误信息:
定义错误信息类:
public class ErrorMessage {
private final int status;
private final String detail;
public ErrorMessage(int status, String detail) {
this.status = status;
this.detail = detail;
}
// Getters and setters
}
拦截器实现:
public class ErrorResponseInterceptor implements Interceptor {
public static final MediaType APPLICATION_JSON = MediaType.get("application/json; charset=utf-8");
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if (!response.isSuccessful()) {
Gson gson = new Gson();
String body = gson.toJson(
new ErrorMessage(response.code(), "The response from the server was not OK"));
ResponseBody responseBody = ResponseBody.create(body, APPLICATION_JSON);
ResponseBody originalBody = response.body();
if (originalBody != null) {
originalBody.close();
}
return response.newBuilder().body(responseBody).build();
}
return response;
}
}
最终返回的 JSON 结构如下:
{
"status": 500,
"detail": "The response from the server was not OK"
}
8. 网络拦截器
除了应用层拦截器,OkHttp 还支持网络层拦截器。两者区别如下:
特性 | 应用拦截器 | 网络拦截器 |
---|---|---|
注册方式 | addInterceptor |
addNetworkInterceptor |
缓存命中时是否执行 | ✅ 总是执行 | ❌ 不执行 |
是否能看到重定向/重试 | ❌ 不可见 | ✅ 可见 |
是否可获取 IP/TLS 信息 | ❌ 否 | ✅ 是 |
注册网络拦截器的方式:
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new SimpleLoggingInterceptor())
.build();
大多数情况下,应用拦截器已经足够使用。
9. 总结
本文介绍了 OkHttp 中拦截器的基本使用方式,包括:
✅ 自定义日志拦截器
✅ 修改响应头
✅ 统一错误处理
✅ 应用拦截器与网络拦截器的区别
拦截器是 OkHttp 中一个非常强大且灵活的功能,合理使用可以极大提升代码的可维护性和扩展性。
完整源码请参考 GitHub 项目。