HTTP协议详解(一):工作原理、请求方法与状态码

为什么要学习 HTTP

HTTP 有一个特点:一次配置,终身使用!

在你接手一个老项目时,你可能不需要很了解 HTTP,你只要仿照之前的代码进行扩展即可,比如新增一个 API 接口。可是,别人一问你 HTTP 相关的知识就懵了。

另外,我们都知道 HTTPS 是安全的,但为什么安全,我们却不知道。一旦超出认知范围,我们就无法确保它的安全。又比如,你可能听过 TCP 的长连接和连接,但"不连接"又是指什么呢?还有,HTTP 传输数据的格式又有哪些?

再举个例子,同一个网络请求可能会有多种写法,在实际应用中,我该使用哪种?

总而言之,对大多数人来说,HTTP 的基础很重要。

HTTP 是什么

对于 HTTP 最直观的印象就是在浏览器地址栏输入地址,然后回车能够打开一个网页。作为安卓工程师,我们会这么认为:通过代码发送网络请求,然后获取响应结果。

HTTP 的官方定义是超文本传输协议(HyperText Transfer Protocol)。

其中 Protocol 在计算机科学中被称为通信协议。HyperText 是超文本,但 Hyper 不是超级的意思,而是扩展的意思。超文本指的是在电脑中显示的、含有可以指向其他文本的链接的文本,其实就是 HTML,用来传输 HTML 文本就是 HTTP 最初的用处。

HTTP 的工作方式

浏览器地址栏输入地址回车,浏览器发起请求到服务器,服务器拿到请求经过处理,响应对应的内容给浏览器,浏览器拿到响应,再将其通过渲染引擎(浏览器内核)渲染成可见的页面。

面对上述过程,我们可能会问:

  1. 地址是怎么转换成请求的?

  2. 请求和响应是什么样的?

首先,请求和响应大概是这样的。

请求:

H 复制代码
GET / HTTP/1.1
Host: example.com

响应:

H 复制代码
HTTP/1.1 200 OK
Server: nginx/1.13.3
Date: Sun, 01 Jul 2021 7:54:55 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Cache-Control: public, max-age=0
ETag: W/"4558-VULd89nkFI4Z8A/TkQk+Strh3nc
Vary: Accept-Encoding
Content-Encoding: gzip

<!DOCTYPE html>
<html>
<head>
...

我们再来看看地址(URL)是怎么转换为请求(报文)的。首先这是一个示例地址:

example.com/users?id=1

它会被浏览器拆成三部分:

  • http://: 协议类型

  • example.com: 服务器地址

  • /users?id=1: 路径(path)以及查询参数

拆解后,会解析成这样:

H 复制代码
GET /users?id=1 HTTP/1.1
Host: example.com

这就是 HTTP 的报文。

再来以安卓开发的角度看看 HTTP 的工作方式,其实和浏览器是一样的。在安卓中,我们通过代码向服务器发送请求,服务器返回响应,我们可以在代码中拿到服务器返回的数据,比如 JSON 格式的数据。但这不是服务器返回的响应报文,而是从响应报文的消息体中取出的一部分数据,

请求报文和响应报文

请求报文格式:Request

那报文是什么样的呢?请求报文大概长这样:

H 复制代码
GET /users HTTP/1.1 // 请求行
Host: example.com // 首部

第一行是请求行,其中 GET 是请求方法(method),/users 是访问的资源路径(path),用于给服务器看的。而下面的 example.com 是给接收到请求的主机看的。HTTP/1.1 是 HTTP 的版本号,主要有几个大版本号,0.9、1.0、1.1、2.0,现在 1.1 是最广泛应用的,0.9、1.0 已被废弃,2.0 也已被广泛应用。

第二行是首部(Header),它是键值对的形式,冒号左边是 key,右边是 value。另外 Header 可以是多行,例如:

H 复制代码
Host: example.com
Content-Type: text/plain
Content-Length: 213

请求报文还可以有 Body,它是报文内部的实质内容,也就是发送给服务器看的具体信息,比如新增用户的请求报文中,Body 部分可以是用户对象序列化后的字符串。

注意:Body 不是必须的,只是请求内容的话,可以不需要。比如使用 GET 请求获取 id 为 1 的用户信息,其请求报文可能为:

H 复制代码
GET /users/1 HTTP/1.1
...
// 并没有 Body 部分

为什么不将 id 放在 body 中,因为不能这么写。规则:path 是用来定位资源的,body 中的数据是用于给服务器处理使用的。

响应报文格式:Response

它的格式和请求报文很类似,也有 header 和 body。

H 复制代码
HTTP/1.1 200 OK // 状态行
content-type: application/json; charset=utf-8
catch-control: public, max-age=60, s-maxage=60
vary: Accept,Accept-Encoding
etag: W/"02eec5b334b0e4c05253d3f4138daa46"
content-encoding: gzip
{"err_no": 0,"err_msg": "success","data": {"id": "7534613534082072586","article_id": "0","user_id": "0","category_id": "0","tag_ids": [],"link_url": "","cover_image": "","is_gfw": 0,"title": "This is title","brief_content": "","is_english": 0,"is_original": 1,"edit_type": 10,"html_content": "deprecated","mark_content": "your_mark_content","ctime": "-62135596800","mtime": "-62135596800","status": 0,"original_type": 0,"theme_ids": []}}

第一行是状态行,其中 200 是状态码,OK 是状态信息,这两个是对这次响应的简单描述。状态码 200 表示请求成功,OK 作为补充的描述。

请求方法

请求方法最重要也最常用的有四个: GET 、POST、PUT、DELETE。

GET

GET 用于获取资源,比如在浏览器地址栏输入 URL 并回车,此时的请求方法绝对是 GET。

GET 也是 HTTP 最初版本中的唯一方法,因为那时只需从服务器中获取数据。另外,它不对服务器数据进行操作(没有提供信息给服务器进行处理),所以请求报文中绝对没有 Body。

如果某个 Web API 的请求中含有 Body,那么它就是不规范的。而这种不规范可能会导致程序崩溃,比如你使用 Retrofit 去请求这样的接口,程序会报错:Method GET must not have a request body.。

POST

POST 用于增加或修改资源,比如新增或修改一个用户。它是一定带有 Body 的,因为要通过 Body 提供信息给服务器去处理。

新增一个用户的请求报文大概长这样:

H 复制代码
POST /users HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 19

name=martin&age=21  // <= Body

前面我们提到:同一个请求可能会有多种写法。POST 请求就是一个很好的例子,其 Body 数据的格式有好几种。这和 Content-Type 这个请求头有关:

  • application/x-www-form-urlencoded: 表单类型,就比如 name=martin&age=21。在 Retrofit 中对应着 @FormUrlEncoded 和 @Field。

  • application/json: 最常使用的,我们可以使用它来传递复杂的数据。在 Retrofit 中对应 @Body。

  • multipart/form-data: 用于上传文件,比如上传图片。在 Retrofit 中对应 @Multipart 和 @Part。

知道了这些,你也就知道为什么同一个请求会有多种写法,并且知道该用哪种:根据后端需要的格式进行选择。

PUT

PUT 用于修改资源,也具有 Body。它和 POST 都可用于修改资源,在实际中选哪个都行。

另外,PUT 和 POST 有个区别,PUT 是幂等的,而 POST 是不具有幂等性的。PUT 多次修改用户,结果都是一样的,用户的信息被修改了。而 POST 多次新增用户,结果不一样,并不是只新增了一个用户,而是新增了多个用户。

GET 也是幂等的,因为它并没有对数据进行修改。

修改用户的请求报文大概长这样:

H 复制代码
PUT /users/1 HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 9

name=jack

DELETE

DELETE 用于删除资源,也是没有 Body。因为删除资源只需定位到对应资源,然后删除即可。

删除一个用户的请求报文大概长这样:

H 复制代码
DELETE /users/1 HTTP/1.1
Host: example.com

DELETE 是幂等的,因为不管多少次,结果都是删除一个用户。

HEAD 是特殊的请求方法,它的使用和 GET 完全相同,只不过它的响应报文没有 Body。

它的用处不是为了获取资源,而是用于获取信息。比如,在真正下载之前,你可以把请求改为 HEAD,因为响应中没有 Body,你可以很快拿到响应。然后从 Header 中得到资源的信息,进一步判断该怎么进行下载。

状态码

状态码是用于对响应结果做出简单的类型化描述,如 200 表示获取成功、404 表示内容未找到。

状态码可被归为以下五类:

  • 1xx: 临时性消息。

    • 101 表示 HTTP 协议切换。用于判断一个服务器是否支持 HTTP 2.0 协议。浏览器会先发送一条 HTTP 1.1协议的请求,并加上 Upgrade: h2,如果服务器支持,响应的状态码会是 101,这样以后浏览器就可以发送 HTTP 2.0 协议的请求了;不支持的话,服务器会忽略这个请求头,响应的状态码是 200,这样浏览器就应该发送 HTTP 1.1 协议的请求。

    • 100 表示继续发送。有时客户端要发送的请求体(Body)会比较大,这时请求头要加上 100-continue,表示我的请求内容还没发完,服务器就会响应 100 的状态码,表示我知道了,请你继续发吧。

  • 2xx: 成功。

    • 如 200 表示请求成功。

    • 201 表示创建成功。

  • 3xx: 重定向。

    • 比如你输入 http 开头的网址,然后回车,会发现竟然跳转到了 https 开头的网址。这是因为浏览器请求了你最开始输入的网站,发现响应的状态码是 301(表示资源永久迁移了),这时,浏览器会再次请求响应头中 Location 所指向的网址。

    • 另外,302 表示临时迁移。

  • 4xx: 客户端(浏览器或是应用)错误,也就是请求有问题。

    • 如请求了不存在的资源,状态码会是 404;

    • 请求了未授权的资源,状态码会是 401。

  • 5xx: 服务器错误。

    • 如服务器宕机了、服务器连接数据库失败,返回的响应码可能是 500、502。

其中最重要的就是 2xx 和 4xx,你只需记住状态码的类型,而不应该去记某一个具体的状态码。

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
XUEYUAN52123 天前
ASN 自治系统号风控:平台如何通过 IP 所属自治域批量识别代理流量
python·网络协议·http·网络安全·socks5
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas