HTTP常见状态码实例讲解

状态码概述

状态码 英文说明 中文说明
1xx Informational 信息提示
2xx Success 成功
3xx Redirection 重定向
4xx Client Error 客户端错误
5xx Server Error 服务端错误

常见状态码

  • 101 Switching
    切换。在WebSocket连接过程协议切换阶段(由http协议转为weboscket协议),服务端返回的状态码就是101,表明服务器已经理解了客户端的请求,并将通过 Upgrade 消息头通知客户端后续请求采用webosocket协议交互。
//request
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ZKMGRsSwZA29ZngvWvq3vQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: permessage-deflate
//response
Connection: upgrade
Upgrade: websocket
Server: nginx/1.18.0 (Ubuntu)
Sec-WebSocket-Accept: 9F9blJQ1AjdV41EOiD2U/LsZOP0=
  • 206 Partial Content
    部分内容。该状态码表示客户端通过发送范围请求头Range抓取到了资源的部分数据。该状态码常用于断点续传下载,服务器的Content-Range响应头表明了返回的是文件的哪一部分,Content-Length响应头表明了该部分文件的大小
curl -I --range 0- http://image.vcapp.cn/image/apk/JuziBrowser_release_1.6.9.1009_100.apk
HTTP常见状态码实例讲解_第1张图片
  • 301 Moved Permanently
    永久性重定向。该状态码表示请求的资源已被分配了新的URI,以后应使用资源现在所指的URI。新的URI从响应头的Location字段里获取。
curl -I http://www.cnblogs.com
  • 302 Found
    临时性重定向。该状态码表示请求的资源暂时被移动到Location响应头所指向的URL上。和301相似,但302表示的资源不是永久移动,只是临时性的。

  • 303 See Other
    该状态码表示由于请求对应的资源存在着另一个URI,应使用GET方法定向获取请求的资源。303和302状态码有着相同的功能,但是303明确表示客户端应当采用GET方法获取资源。

  • 307 Temporary Redirect
    临时性重定向,HTTP 1.1的状态码。状态码 307 与 302 之间的区别在于,当发送重定向请求的时候,307 状态码可以确保请求方法和消息主体不会发生变化。当响应状态码为 302 的时候,一些旧有的用户代理会错误地将请求方法转换为GET。

  • 308 Permanent Redirect
    永久性重定向,HTTP 1.1的状态码。状态码308类似于301,但不允许将请求方法从POST更改为GET。

  • 403 Forbidden
    禁止访问。该状态码表示服务器理解客户的请求,但拒绝处理它

curl -I http://www.163.com
HTTP常见状态码实例讲解_第2张图片

OkHttp中状态码的处理

  • 判断HTTP请求是否成功

当code>=200 && code<300时认为是成功

  /**
   * Returns true if the code is in [200..300), which means the request was successfully received,
   * understood, and accepted.
   */
  val isSuccessful: Boolean
    get() = code in 200..299
  • 判断HTTP请求是否为重定向
  /** Returns true if this response redirects to another resource. */
  val isRedirect: Boolean
    get() = when (code) {
      HTTP_PERM_REDIRECT,     //308
      HTTP_TEMP_REDIRECT,     //307
      HTTP_MULT_CHOICE,       //300
      HTTP_MOVED_PERM,        //301
      HTTP_MOVED_TEMP,        //302 
      HTTP_SEE_OTHER          //303
      -> true
      else -> false
    }
  • 重定向处理

RetryAndFollowUpInterceptorintercept方法里判断Response是否需要重定向,如需要重定向就重新组装Request,然后重新发起请求。

 @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    var request = chain.request
    val call = realChain.call
    var followUpCount = 0
    var priorResponse: Response? = null
    var newExchangeFinder = true
    var recoveredFailures = listOf()
    while (true) {
      call.enterNetworkInterceptorExchange(request, newExchangeFinder)

      var response: Response
      var closeActiveExchange = true
      try {
        if (call.isCanceled()) {
          throw IOException("Canceled")
        }

        try {
          response = realChain.proceed(request)
          newExchangeFinder = true
        } catch (e: RouteException) {
          // The attempt to connect via a route failed. The request will not have been sent.
          if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
            throw e.firstConnectException.withSuppressed(recoveredFailures)
          } else {
            recoveredFailures += e.firstConnectException
          }
          newExchangeFinder = false
          continue
        } catch (e: IOException) {
          // An attempt to communicate with a server failed. The request may have been sent.
          if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
            throw e.withSuppressed(recoveredFailures)
          } else {
            recoveredFailures += e
          }
          newExchangeFinder = false
          continue
        }

        // Attach the prior response if it exists. Such responses never have a body.
        if (priorResponse != null) {
          response = response.newBuilder()
              .priorResponse(priorResponse.newBuilder()
                  .body(null)
                  .build())
              .build()
        }

        val exchange = call.interceptorScopedExchange
        val followUp = followUpRequest(response, exchange)

        if (followUp == null) {
          if (exchange != null && exchange.isDuplex) {
            call.timeoutEarlyExit()
          }
          closeActiveExchange = false
          return response
        }

        val followUpBody = followUp.body
        if (followUpBody != null && followUpBody.isOneShot()) {
          closeActiveExchange = false
          return response
        }

        response.body?.closeQuietly()

        if (++followUpCount > MAX_FOLLOW_UPS) {
          throw ProtocolException("Too many follow-up requests: $followUpCount")
        }

        request = followUp
        priorResponse = response
      } finally {
        call.exitNetworkInterceptorExchange(closeActiveExchange)
      }
    }
  }

判断是否需要重定向的逻辑在followUpRequest方法中

  /**
   * Figures out the HTTP request to make in response to receiving [userResponse]. This will
   * either add authentication headers, follow redirects or handle a client request timeout. If a
   * follow-up is either unnecessary or not applicable, this returns null.
   */
  @Throws(IOException::class)
  private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
    val route = exchange?.connection?.route()
    val responseCode = userResponse.code

    val method = userResponse.request.method
    when (responseCode) {
      HTTP_PROXY_AUTH -> {
        val selectedProxy = route!!.proxy
        if (selectedProxy.type() != Proxy.Type.HTTP) {
          throw ProtocolException("Received HTTP_PROXY_AUTH (407) code while not using proxy")
        }
        return client.proxyAuthenticator.authenticate(route, userResponse)
      }

      HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse)

      HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
        return buildRedirectRequest(userResponse, method)
      }

      HTTP_CLIENT_TIMEOUT -> {
        // 408's are rare in practice, but some servers like HAProxy use this response code. The
        // spec says that we may repeat the request without modifications. Modern browsers also
        // repeat the request (even non-idempotent ones.)
        if (!client.retryOnConnectionFailure) {
          // The application layer has directed us not to retry the request.
          return null
        }

        val requestBody = userResponse.request.body
        if (requestBody != null && requestBody.isOneShot()) {
          return null
        }
        val priorResponse = userResponse.priorResponse
        if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {
          // We attempted to retry and got another timeout. Give up.
          return null
        }

        if (retryAfter(userResponse, 0) > 0) {
          return null
        }

        return userResponse.request
      }

      HTTP_UNAVAILABLE -> {
        val priorResponse = userResponse.priorResponse
        if (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) {
          // We attempted to retry and got another timeout. Give up.
          return null
        }

        if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
          // specifically received an instruction to retry without delay
          return userResponse.request
        }

        return null
      }

      HTTP_MISDIRECTED_REQUEST -> {
        // OkHttp can coalesce HTTP/2 connections even if the domain names are different. See
        // RealConnection.isEligible(). If we attempted this and the server returned HTTP 421, then
        // we can retry on a different connection.
        val requestBody = userResponse.request.body
        if (requestBody != null && requestBody.isOneShot()) {
          return null
        }

        if (exchange == null || !exchange.isCoalescedConnection) {
          return null
        }

        exchange.connection.noCoalescedConnections()
        return userResponse.request
      }

      else -> return null
    }
  }

如果响应码是307,308,300,301,302,303时调用buildRedirectRequest方法组建重定向的Request

  @JvmStatic // Despite being 'internal', this method is called by popular 3rd party SDKs.
  fun permitsRequestBody(method: String): Boolean = !(method == "GET" || method == "HEAD")

  fun redirectsWithBody(method: String): Boolean =
      // (WebDAV) redirects should also maintain the request body
      method == "PROPFIND"

  fun redirectsToGet(method: String): Boolean =
      // All requests but PROPFIND should redirect to a GET request.
      method != "PROPFIND"

  private fun buildRedirectRequest(userResponse: Response, method: String): Request? {
    // Does the client allow redirects?
    if (!client.followRedirects) return null

    val location = userResponse.header("Location") ?: return null
    // Don't follow redirects to unsupported protocols.
    val url = userResponse.request.url.resolve(location) ?: return null

    // If configured, don't follow redirects between SSL and non-SSL.
    val sameScheme = url.scheme == userResponse.request.url.scheme
    if (!sameScheme && !client.followSslRedirects) return null

    // Most redirects don't include a request body.
    val requestBuilder = userResponse.request.newBuilder()
    if (HttpMethod.permitsRequestBody(method)) {
      val responseCode = userResponse.code
      val maintainBody = HttpMethod.redirectsWithBody(method) ||
          responseCode == HTTP_PERM_REDIRECT ||
          responseCode == HTTP_TEMP_REDIRECT
      if (HttpMethod.redirectsToGet(method) && responseCode != HTTP_PERM_REDIRECT && responseCode != HTTP_TEMP_REDIRECT) {
        requestBuilder.method("GET", null)
      } else {
        val requestBody = if (maintainBody) userResponse.request.body else null
        requestBuilder.method(method, requestBody)
      }
      if (!maintainBody) {
        requestBuilder.removeHeader("Transfer-Encoding")
        requestBuilder.removeHeader("Content-Length")
        requestBuilder.removeHeader("Content-Type")
      }
    }

    // When redirecting across hosts, drop all authentication headers. This
    // is potentially annoying to the application layer since they have no
    // way to retain them.
    if (!userResponse.request.url.canReuseConnectionFor(url)) {
      requestBuilder.removeHeader("Authorization")
    }

    return requestBuilder.url(url).build()
  }
  1. 根据followRedirects字段判断是否允许重定向,默认为true,如不允许重定向,则返回null
  2. 重定向的地址从响应头Location字段中获取
  3. 根据followSslRedirects字段判断是否允许跨域重定向,如:HTTP -> HTTPS,如不允许,则返回null
  4. 如果请求方法不是GET或者HEAD
    • 如果请求方法不是PROPFIND或者响应码是307308则保留RequestBody

    • 如果请求方法是PROPFIND并且响应码不是307308直接使用GET请求方法,RequestBody=null,其余情况请求方法为原本Request的请求方法,如果保留RequestBody则填充原本的RequestBody到新的Request

HttpURLConnection

在HttpURLConnection类中定义了很多状态码常量,如下:

常量 说明
HTTP_OK 200 OK
HTTP_CREATED 201 Created
HTTP_ACCEPTED 202 Accepted
HTTP_NOT_AUTHORITATIVE 203 Non-Authoritative Information
HTTP_NO_CONTENT 204 No Content
HTTP_RESET 205 Reset Content
HTTP_PARTIAL 206 Partial Content
HTTP_MULT_CHOICE 300 Multiple Choices
HTTP_MOVED_PERM 301 Moved Permanently
HTTP_MOVED_TEMP 302 Temporary Redirect
HTTP_SEE_OTHER 303 See Other
HTTP_NOT_MODIFIED 304 Not Modified
HTTP_USE_PROXY 305 Use Proxy
HTTP_BAD_REQUEST 400 Bad Request
HTTP_UNAUTHORIZED 401 Unauthorized
HTTP_PAYMENT_REQUIRED 402 Payment Required
HTTP_FORBIDDEN 403 Forbidden
HTTP_NOT_FOUND 404 Not Found
HTTP_BAD_METHOD 405 Method Not Allowed
HTTP_NOT_ACCEPTABLE 406 Not Acceptable
HTTP_PROXY_AUTH 407 Proxy Authentication Required
HTTP_CLIENT_TIMEOUT 408 Request Time-Out
HTTP_CONFLICT 409 Conflict
HTTP_GONE 410 Gone
HTTP_LENGTH_REQUIRED 411 Length Required
HTTP_PRECON_FAILED 412 Precondition Failed
HTTP_ENTITY_TOO_LARGE 413 Request Entity Too Large
HTTP_REQ_TOO_LONG 414 Request-URI Too Large
HTTP_UNSUPPORTED_TYPE 415 Unsupported Media Type
HTTP_INTERNAL_ERROR 500 Internal Server Error
HTTP_NOT_IMPLEMENTED 501 Not Implemented
HTTP_BAD_GATEWAY 502 Bad Gateway
HTTP_UNAVAILABLE 503 Service Unavailable
HTTP_GATEWAY_TIMEOUT 504 Gateway Timeout
HTTP_VERSION 505 HTTP Version Not Supported

你可能感兴趣的:(HTTP常见状态码实例讲解)