美文网首页
HTTP常见状态码实例讲解

HTTP常见状态码实例讲解

作者: 孔鹏飞 | 来源:发表于2021-04-16 12:28 被阅读0次

    状态码概述

    状态码 英文说明 中文说明
    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
    • 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

    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<IOException>()
        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常见状态码实例讲解

          本文链接:https://www.haomeiwen.com/subject/gzyqlltx.html