BridgeIntercept
还没写完,顺序会有些乱,那篇写完发那篇
主要作用
连接桥路由.
-
添加一些必须的请求头信息
-
对服务器返回的数据进行解压缩(如果服务端返回的响应的编码请求头(Content-Encoding)是(gzip)格式的话,如果不是则直接返回)
源码解析
/**
* @param cookieJar 我们在初始化OkHttpClient的时候设置的CookieJar
*/
class BridgeInterceptor(private val cookieJar: CookieJar) : Interceptor {
/**
* 实现于Interceptor的方法
* @param chain 链表
*/
override fun intercept(chain: Interceptor.Chain): Response {
// 获取用户请求
val userRequest = chain.request()
// 根据用户请求重新获取一个RequestBuilder
val requestBuilder = userRequest.newBuilder()
// 获取请求的body信息
val body = userRequest.body
// 如果body不为空
if (body != null) {
// 获取body的类型,application/json application/xml application/x-www-form-urlencoded multipart/form-data等
val contentType = body.contentType()
// 如果这个contentType不为空,则添加这个类型
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString())
}
// 获取body的长度
val contentLength = body.contentLength()
// 如果这个body的长度不为空
if (contentLength != -1L) {
// 添加请求体长度的请求头
requestBuilder.header("Content-Length", contentLength.toString())
// 移除不能确定请求体长度的请求头
requestBuilder.removeHeader("Transfer-Encoding")
} else {
// 相反,添加不能确定请求体长度的请求头
requestBuilder.header("Transfer-Encoding", "chunked")
// 移除请求体长度的请求头
requestBuilder.removeHeader("Content-Length")
}
}
// 如果请求的网络域名为空
if (userRequest.header("Host") == null) {
// 则添加请求相关网络域名的请求头
requestBuilder.header("Host", userRequest.url.toHostHeader())
}
// 如果请求的连接为空的话
if (userRequest.header("Connection") == null) {
// 则添加相关的请求头
requestBuilder.header("Connection", "Keep-Alive")
}
// 定义变量,是否进行GZIP压缩数据
var transparentGzip = false
// 如果获取到的请求关于是否压缩的请求头为空
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
// 那么我们将这个变量定义为true
transparentGzip = true
// 且添加相关的请求头
requestBuilder.header("Accept-Encoding", "gzip")
}
// 我们从设置的CookieJar中根据请求地址获取相关的cookie
val cookies = cookieJar.loadForRequest(userRequest.url)
// 如果cookie为空
if (cookies.isNotEmpty()) {
// 那么我们添加cookie的请求头
requestBuilder.header("Cookie", cookieHeader(cookies))
}
// 如果用户得UserAgent请求头为空的话,那么我们就添加系统默认的User-Agent:okhttp/okhttp-version
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}
// 通过调用链的层层调用获取最终返回的响应.且看这里发送的可是我们包装过的request哦!!!!
val networkResponse = chain.proceed(requestBuilder.build())
// 调用CookieJar的扩展方法,等下看下面的解释吧
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
// 根据返回的响应我们重新构建出一个响应来
val responseBuilder = networkResponse.newBuilder()
.request(userRequest)
/**
* 处理响应数据,如果我们没有在请求头添加 Content-Encoding 和 Range 请求头,那么我们的transparentGzip则为true,反之
* false
* 如果服务器返回的响应的响应头中包含Content-Encoding 请求头,且这个值为gzip
* 如果这个响应的响应体有可能为0
* 我们就对数据进行gzip解压缩,否则就不处理数据
*/
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()) {
// 获取到响应体
val responseBody = networkResponse.body
// 如果响应体不为空
if (responseBody != null) {
// 通过响应体构建出GzipSource
val gzipSource = GzipSource(responseBody.source())
// 重新构建出一个响应头,需要移除对应的两个请求头,因为都属于gzip,肯定长度啥的没了
val strippedHeaders = networkResponse.headers.newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build()
// 将响应头给到响应
responseBuilder.headers(strippedHeaders)
// 构建出用户使用的ResponseBody
val contentType = networkResponse.header("Content-Type")
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
// 最后返回响应到上一个拦截器,应该就是RetryAndFollowUpInterceptor
return responseBuilder.build()
}
/**
* Returns a 'Cookie' HTTP request header with all cookies, like `a=b; c=d`.
*
* 根据用户保存的Cookie,通过StringBuilder,获取到cookie字符串.例如: a=b; c=d
*/
private fun cookieHeader(cookies: List<Cookie>): String = buildString {
cookies.forEachIndexed { index, cookie ->
if (index > 0) append("; ")
append(cookie.name).append('=').append(cookie.value)
}
}
}
额外扩展
CookieJar的扩展方法receiveHeaders
/**
* 总体来说,这个方法是对返回的响应的Cookie的处理
*/
fun CookieJar.receiveHeaders(url: HttpUrl, headers: Headers) {
// 如果CookieJar的策略是不缓存Cookie,那么我们直接进行返回
if (this === CookieJar.NO_COOKIES) return
// 如果不是,我们对服务器返回的Cookie进行解析,获取到一个List<Cookie>
val cookies = Cookie.parseAll(url, headers)
// 如果解析到的Cookies是空,那么就返回
if (cookies.isEmpty()) return
// 如果解析到的Cookies不为空,那么我们就进行存储,而这个方法属于CookieJar的方法,我们在实现CookieJar的时候,会实现该方法
saveFromResponse(url, cookies)
}
上面BridgeIntercept涉及的Header
-
Content-Type
内容类型,用于定义网络文件的类型和网页的编码,决定采用什么形式和什么编码来读取这个文件
常见的媒体类型:
text/html :HTML格式
text/plain :纯文本格式
text/xml : XML格式
image/gif :gif格式图片
image/jpeg :jpg格式图片
image/png :png格式图片
以application开头的媒体格式类型:
application/json :JSON数据格式
application/xhtml+xml : XHTML格式
application/xml :XML数据格式
application/pdf :pdf格式
application/msword :word文档格式
application/octet-stream :二进制流数据(如常见的文件下载)
application/x-www-form-urlencoded :常见的表单上传
另一种媒体长传格式类型:
multipart/from-data :需要在表单中进行文件上传时,就需要使用此格式
tips:
这个请求头是你自己已经添加过了,那么就不在需要添加了.如果你没有写,库会根据你的请求体的类型,给你进行添加
-
Content-Length
内容长度
tips:
这个库会主动给你添加,无论你是否有写
如果你的长度是没有的话,库会删除这个请求头. 如果你的长度是有的话,库会根据请求体的body从而进行赋值
-
Transfer-Encoding
内容长度不确定,常用值为:chunked
tips:
跟上面的Content-Length一块出现,但是属于老死不相往来的那种,有你没我,有我没你.体现在代码上就是,有长度就没我,没有长度就有我
-
Host
网络域名
tips:
就是指请求的域名
这个请求头也是,如果你自己添加了,库就不会再给你添加了
-
Connection
是否需要保持连接.一般值为Keep-Alive.
tips:
解释: https://blog.csdn.net/xiaoduanayu/article/details/78386508
这个请求头也是,如果你自己添加了,库就不会再给你添加了
-
Accept-Encoding、Range
设置编码方式
tips:
不管有没有用GZIP压缩请求内容,请不要动这个值,否则你会发现,你设置了GZIP压缩,结果乱码,还不能用了.
因为源码中设置了如果你没有设置这两个请求头的时候,transparentGzip才会为true,而在响应来的时候,会根据这个值进行判断,如果为true,才可以进去,否则你就是设置了也是乱码,注意注意!!!
-
Cookie
用户标记
tips:
库不会给你设置,只能你自己来
-
User-Agent
用户代理,设置了你访问连接的方式,比如手机还是浏览器
tips:
这个请求头也是,如果你自己添加了,库就不会再给你添加了
总结:
- 将我们的请求进行了包装,添加了一些请求头的信息
- 对服务器返回的数据进行解压缩(如果服务端返回的响应的编码请求头(Content-Encoding)是(gzip)格式的话,如果不是则直接返回).
- Cookie需要自己去实现,OkHttp只提供了接口
- 结束了!