五、OkHttp拦截器CallServerInterceptor

CallServerInterceptor是OkHttp中处理网络请求的关键拦截器,负责向服务器发送请求。它涉及到了请求头的写入、Expect:100-continue机制的处理、请求体的写入以及响应的读取。对于HTTP/1.1的100-continue响应,拦截器会做特殊处理,同时它也处理WebSocket连接升级的情况。在处理完响应后,拦截器会检查响应码,如204或205状态码伴随非零Content-Length,会抛出协议异常。

CallServerInterceptor是拦截器链中的最后一个拦截器,它负责向服务器发送器请求。
先看源码:

class CallServerInterceptor(private val forWebSocket: Boolean) : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    // 将拦截器链转换为实际的拦截器链对象
    val realChain = chain as RealInterceptorChain
    // 获取与服务器进行交互的Exchange对象
    val exchange = realChain.exchange!!
    // 获取请求对象
    val request = realChain.request
    // 获取请求体对象
    val requestBody = request.body
    // 记录请求发送的时间
    val sentRequestMillis = System.currentTimeMillis()

    // 写入请求头部
    exchange.writeRequestHeaders(request)

    // 初始化变量
    var invokeStartEvent = true
    var responseBuilder: Response.Builder? = null

    // 如果请求方法允许请求体,并且存在请求体
    if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) {
      // HTTP header 中的 Expect 字段用于指示客户端对服务器的期望行为。它允许客户端在发送请求之前,告知服务器应如何处理请求。
      // Expect: 100-continue
      // 100-continue:客户端希望在发送请求正文之前,服务器先发送一个 100 Continue 的响应,用于确认服务器是否愿意接收请求正文。
      // 如果服务器返回了 100 Continue 响应,客户端可以继续发送请求正文;
      // 如果服务器返回其他响应码,客户端则可以终止请求,而无需发送整个请求正文。
      
      // 如果请求头部包含 "Expect: 100-continue",等待 "HTTP/1.1 100 Continue" 响应后再发送请求体
      if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
        exchange.flushRequest()
        responseBuilder = exchange.readResponseHeaders(expectContinue = true)
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
      
      // 如果响应构建器为空,说明 "Expect: 100-continue" 预期没有被满足
      if (responseBuilder == null) {
        if (requestBody.isDuplex()) {
          // isDuplex() 方法用于判断当前请求体是否支持双向通信。
          // 如果返回值为 true,表示该请求体支持双向通信,可以在发送请求后继续向服务器发送数据。
          // 这通常用于实现类似 WebSocket 连接等需要进行实时双向通信的场景。
          
          // 如果返回值为 false,表示该请求体不支持双向通信,只能在发送请求时将请求数据发送给服务器,
          // 无法在请求发送后继续发送额外的数据。
          exchange.flushRequest()
          val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
          requestBody.writeTo(bufferedRequestBody)
        } else {
          // 写入请求体
          val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
          requestBody.writeTo(bufferedRequestBody)
          bufferedRequestBody.close()
        }
      } else {
        exchange.noRequestBody()
        if (!exchange.connection.isMultiplexed) {
          // 如果 "Expect: 100-continue" 预期未被满足,防止复用 HTTP/1 连接
          exchange.noNewExchangesOnConnection()
        }
      }
    } else {
      exchange.noRequestBody()
    }

    // 完成请求
    if (requestBody == null || !requestBody.isDuplex()) {
      exchange.finishRequest()
    }

    // 如果响应构建器为空,读取响应头部并进行处理
    if (responseBuilder == null) {
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
        invokeStartEvent = false
      }
    }

    // 获取响应对象
    var response = responseBuilder
        .request(request)
        .handshake(exchange.connection.handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build()

    var code = response.code

    //如果响应码为 100,表示服务器发送了一个 100-continue 响应,但我们没有请求它。此时需要重新读取实际的响应状态。
   if (code == 100) {
      // 服务器发送了一个 100-continue 响应,尽管我们没有请求。重新尝试读取实际的响应状态。
      responseBuilder = exchange.readResponseHeaders(expectContinue = false)!!
      if (invokeStartEvent) {
        exchange.responseHeadersStart()
      }
      response = responseBuilder
          .request(request)
          .handshake(exchange.connection.handshake())
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(System.currentTimeMillis())
          .build()
      code = response.code
    }

    // 结束响应头部的处理
    exchange.responseHeadersEnd(response)

    // 如果为 WebSocket 并且响应码为 101,表示正在进行协议升级
    response = if (forWebSocket && code == 101) {
      // 连接正在升级,但我们需要确保拦截器看到一个非空的响应体。
      response.newBuilder()
          .body(EMPTY_RESPONSE)
          .build()
    } else {
        // 写入响应体
      response.newBuilder()
          .body(exchange.openResponseBody(response))
          .build()
    }

    // 如果请求或响应头部包含 "Connection: close",防止在此连接上创建新的请求
    if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
        "close".equals(response.header("Connection"), ignoreCase = true)) {
      exchange.noNewExchangesOnConnection()
    }

    // 如果响应码为 204 或 205,并且响应体长度大于 0,则抛出协议异常
    if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) {
      throw ProtocolException(
          "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}")
    }

    return response
  }
}

CallServerInterceptor是真正执行网络请求的地方,
通过ConnectInterceptor 连接拦截器获得Socket inputSteram和outputStream 并包装成了source、sink

// val source = InputStreamSource(getInputStream(), timeout)
source = rawSocket.source().buffer()
// val sink = OutputStreamSink(getOutputStream(), timeout)
sink = rawSocket.sink().buffer()

至此,Socket的连接已经建立。之后就是通过CallServerInterceptor 进行数据流的读写,

// 写入请求
requestBody.writeTo(bufferedRequestBody)
// 读取响应流
exchange.openResponseBody(response)

当读取到响应流 并包装成Response 返回后,OkHttp的完整请求至此结束!

OkHttp中有多种内置拦截器,以下是对常见拦截器及其实现步骤的详细介绍,并附有方法注释: ### 重试与重定向拦截器(RetryAndFollowUpInterceptor) 该拦截器负责处理请求失败时的重试和重定向逻辑。 ```java import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; // 模拟重试与重定向拦截器 class RetryAndFollowUpInterceptor implements Interceptor { private final int maxRetryCount; // 最大重试次数 public RetryAndFollowUpInterceptor(int maxRetryCount) { this.maxRetryCount = maxRetryCount; } @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); int retryCount = 0; IOException lastException = null; // 重试逻辑 while (retryCount <= maxRetryCount) { try { // 调用下一个拦截器处理请求 return chain.proceed(request); } catch (IOException e) { lastException = e; retryCount++; } } // 若重试次数达到上限,抛出最后一次异常 throw lastException; } } ``` #### 方法注释: - `intercept(Chain chain)`:该方法是拦截器的核心方法,接收一个 `Chain` 对象,用于获取原始请求并调用下一个拦截器处理请求。在方法内部,通过循环实现重试逻辑,若请求失败则捕获异常并增加重试次数,若重试次数达到上限则抛出最后一次异常。 ### 桥接拦截器(BridgeInterceptor) 桥接拦截器负责将用户的请求转换为网络请求,添加必要的请求头。 ```java import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; // 模拟桥接拦截器 class BridgeInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request userRequest = chain.request(); Request.Builder requestBuilder = userRequest.newBuilder(); // 添加必要的请求头 requestBuilder.addHeader("Content-Type", "application/json"); requestBuilder.addHeader("Accept", "application/json"); Request networkRequest = requestBuilder.build(); // 调用下一个拦截器处理请求 Response networkResponse = chain.proceed(networkRequest); return networkResponse; } } ``` #### 方法注释: - `intercept(Chain chain)`:该方法接收一个 `Chain` 对象,获取用户请求并创建请求构建器。通过请求构建器添加必要的请求头,如 `Content-Type` 和 `Accept`,然后构建网络请求并调用下一个拦截器处理请求,最后返回响应。 ### 缓存拦截器(CacheInterceptor) 缓存拦截器负责处理请求的缓存逻辑,若缓存命中则直接返回缓存响应,否则继续执行后续拦截器。 ```java import okhttp3.CacheControl; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; // 模拟缓存拦截器 class CacheInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // 构建缓存控制策略 CacheControl cacheControl = new CacheControl.Builder() .maxAge(60, java.util.concurrent.TimeUnit.SECONDS) // 缓存最大有效期为60秒 .build(); Request cachedRequest = request.newBuilder() .cacheControl(cacheControl) .build(); // 调用下一个拦截器处理请求 Response networkResponse = chain.proceed(cachedRequest); // 可以在这里处理缓存响应 return networkResponse; } } ``` #### 方法注释: - `intercept(Chain chain)`:该方法接收一个 `Chain` 对象,获取原始请求并构建缓存控制策略。通过请求构建器应用缓存控制策略,然后调用下一个拦截器处理请求,最后返回响应。可以在方法内部添加逻辑处理缓存响应。 ### 连接拦截器(ConnectInterceptor) 连接拦截器负责建立与服务器的连接。 ```java import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; // 模拟连接拦截器 class ConnectInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // 建立与服务器的连接 // 这里省略具体的连接建立逻辑 // 调用下一个拦截器处理请求 return chain.proceed(request); } } ``` #### 方法注释: - `intercept(Chain chain)`:该方法接收一个 `Chain` 对象,获取原始请求,在方法内部可以实现建立与服务器的连接逻辑,然后调用下一个拦截器处理请求,最后返回响应。 ### 网络拦截器CallServerInterceptor) 网络拦截器负责向服务器发送请求并接收响应。 ```java import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; // 模拟网络拦截器 class CallServerInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // 向服务器发送请求 // 这里省略具体的请求发送逻辑 // 接收服务器响应 // 这里省略具体的响应接收逻辑 // 构建响应对象 Response response = new Response.Builder() .request(request) .protocol(okhttp3.Protocol.HTTP_1_1) .code(200) .message("OK") .build(); return response; } } ``` #### 方法注释: - `intercept(Chain chain)`:该方法接收一个 `Chain` 对象,获取原始请求,在方法内部实现向服务器发送请求和接收响应的逻辑,然后构建响应对象并返回。 ### 使用示例 ```java import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { // 创建拦截器实例 RetryAndFollowUpInterceptor retryInterceptor = new RetryAndFollowUpInterceptor(3); BridgeInterceptor bridgeInterceptor = new BridgeInterceptor(); CacheInterceptor cacheInterceptor = new CacheInterceptor(); ConnectInterceptor connectInterceptor = new ConnectInterceptor(); CallServerInterceptor callServerInterceptor = new CallServerInterceptor(); // 创建 OkHttpClient 实例并添加拦截器 OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(retryInterceptor) .addInterceptor(bridgeInterceptor) .addInterceptor(cacheInterceptor) .addInterceptor(connectInterceptor) .addInterceptor(callServerInterceptor) .build(); // 创建请求 Request request = new Request.Builder() .url("https://www.example.com") .build(); // 发送请求并获取响应 Response response = client.newCall(request).execute(); // 输出响应信息 System.out.println(response.body().string()); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值