Retrofit2拦截器设置及响应无法拦截的问题

前言

我们在添加响应切面后原接口无法接收响应的问题,核心原因是

OkHttp 的 ResponseBody 是一次性流(仅能读取一次)

也就是说代码中调用了 body.string() 读取响应体,导致流被耗尽,后续 Retrofit 解析响应时,流已无数据可读,最终接口无法获取结果。

这里的解决方法是

重新构建 ResponseBody

拦截器

完整代码

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import android.util.Log
import com.google.gson.Gson
import com.xhkjedu.zxs_android.common.CommonData
import com.xhkjedu.zxs_android.model.ResultVoNoData
import com.xhkjedu.zxs_android.utils.SPUtil
import com.xhkjedu.zxs_android.utils.eventbus.ZEventExitLogin
import com.xhkjedu.zxs_android.utils.eventbus.ZFlowEventBus
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.ResponseBody
import okhttp3.ResponseBody.Companion.toResponseBody


class HeaderInterceptor : Interceptor {
val TAG: String = "HeaderInterceptor"

override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
try {
val mBuilder = original.newBuilder()
if (CommonData.Authorization != "") {
mBuilder.header("Authorization", CommonData.Authorization)
}

val request = mBuilder
.method(original.method, original.body)
.build()

val response = chain.proceed(request)
// 获取响应头
val headers = response.headers
// 处理响应头
headers.forEach { header ->
if (header.first == "Authorization") {
CommonData.Authorization = header.second
SPUtil.putString("Authorization", header.second)
}
}

return handleResponse(response)
} catch (_: Exception) {
return chain.proceed(original)
}
}

class InterceptorCoroutineScope : CoroutineScope {
// 核心:通过Job控制作用域生命周期,取消Job则作用域内所有协程终止
private val job = Job()

// 配置协程调度器(根据需求选择,如IO调度器处理网络/本地存储)
override val coroutineContext = Dispatchers.IO + job + CoroutineName("InterceptorScope")

// 取消作用域(拦截器结束时调用)
fun cancelScope() {
job.cancel()
}
}

/**
* 响应统一处理
* @param response 原始响应
* @return 处理后的响应(可修改响应内容或直接返回原始响应)
*/
private fun handleResponse(response: Response): Response {
val statusCode = response.code
if (statusCode == 200) {
val body = response.body
body?.let {
val contentType = it.contentType()?.toString() ?: ""
if (contentType.contains("application/json")) {
val responseStr = it.string()
try {
val resultVoNoData =
Gson().fromJson(responseStr, ResultVoNoData::class.java)
if (resultVoNoData.code == 2) {
// 创建临时协程作用域(拦截器执行完后会取消)
val scope = InterceptorCoroutineScope()
try {
runBlocking(scope.coroutineContext) {
launch {
ZFlowEventBus.post(ZEventExitLogin())
}
}
} finally {
scope.cancelScope()
}
}
} catch (_: Exception) {
Log.e(TAG, "handleResponse: 解析失败")
}
val newBody: ResponseBody? = responseStr
.toResponseBody(it.contentType())
return response.newBuilder().body(newBody).build()
}
}

}
return response
}
}

响应处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 响应统一处理
* @param response 原始响应
* @return 处理后的响应(可修改响应内容或直接返回原始响应)
*/
private fun handleResponse(response: Response): Response {
val statusCode = response.code
if (statusCode == 200) {
val body = response.body
if (body != null) {
val responseStr = body.string()
// 下面可以写自定义响应处理的逻辑

// 重建ResponseBody
val newBody: ResponseBody? = responseStr
.toResponseBody(it.contentType())
return response.newBuilder().body(newBody).build()
}
}
return response
}

自定义协程

1
2
3
4
5
6
7
8
9
10
11
12
class InterceptorCoroutineScope : CoroutineScope {
// 核心:通过Job控制作用域生命周期,取消Job则作用域内所有协程终止
private val job = Job()

// 配置协程调度器(根据需求选择,如IO调度器处理网络/本地存储)
override val coroutineContext = Dispatchers.IO + job + CoroutineName("InterceptorScope")

// 取消作用域(拦截器结束时调用)
fun cancelScope() {
job.cancel()
}
}

协程使用

1
2
3
4
5
6
7
8
9
10
11
// 创建临时协程作用域(拦截器执行完后会取消)
val scope = InterceptorCoroutineScope()
try {
runBlocking(scope.coroutineContext) {
launch {
ZFlowEventBus.post(ZEventExitLogin())
}
}
} finally {
scope.cancelScope()
}

设置拦截器

完整代码

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
import android.annotation.SuppressLint
import com.xhkjedu.zxs_android.common.CommonData
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.security.SecureRandom
import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager


object ApiManager {
private lateinit var commonRetrofit: Retrofit

lateinit var userService: ServiceUser


init {
if (!::commonRetrofit.isInitialized) {
val client = getUnsafeOkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.addInterceptor(HeaderInterceptor())
.build();

commonRetrofit = Retrofit.Builder()
.baseUrl(CommonData.apiUrl)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}

if (!::userService.isInitialized) {
userService = commonRetrofit.create(ServiceUser::class.java)
}

}

@SuppressLint("CustomX509TrustManager")
fun getUnsafeOkHttpClient(): OkHttpClient {
try {
// 创建一个信任所有证书的 TrustManager
val trustAllCerts = arrayOf<TrustManager>(

object : X509TrustManager {
override fun checkClientTrusted(
chain: Array<out java.security.cert.X509Certificate>?,
authType: String?
) {
}

override fun checkServerTrusted(
chain: Array<out java.security.cert.X509Certificate>?,
authType: String?
) {
}

override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> =
arrayOf()
}
)

// 初始化 SSLContext
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustAllCerts, SecureRandom())

// 创建一个允许所有主机名验证的 HostnameVerifier
val allHostsValid = HostnameVerifier { _, _ -> true }

// 创建 OkHttpClient 并配置 SSL 和主机名验证
return OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier(allHostsValid)
.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}

跳过证书验证

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
@SuppressLint("CustomX509TrustManager")
fun getUnsafeOkHttpClient(): OkHttpClient {
try {
// 创建一个信任所有证书的 TrustManager
val trustAllCerts = arrayOf<TrustManager>(

object : X509TrustManager {
override fun checkClientTrusted(
chain: Array<out java.security.cert.X509Certificate>?,
authType: String?
) {
}

override fun checkServerTrusted(
chain: Array<out java.security.cert.X509Certificate>?,
authType: String?
) {
}

override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> =
arrayOf()
}
)

// 初始化 SSLContext
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustAllCerts, SecureRandom())

// 创建一个允许所有主机名验证的 HostnameVerifier
val allHostsValid = HostnameVerifier { _, _ -> true }

// 创建 OkHttpClient 并配置 SSL 和主机名验证
return OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier(allHostsValid)
.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST

interface ServiceUser {

data class LoginPara(
var username: String = "", // 用户名
var password: String = "", // 密码
)

@POST("/api/auth/login")
suspend fun apiAuthLogin(@Body request: LoginPara): Response<ResultVo<UserModel>>
}