前段时间去大佬何红辉(Android设计模式作者)的公司面试,前两轮还算顺利。等面到大佬的时候完全就是被虐的节奏。有些知识你觉得掌握自己掌握了,但是一些细节推敲起来你会发现自己掌握的那些还是没有get到点子上。所以说学知识一定要带着问题去学,并思考开发者为什么要这样设计。学到的这些知识一定要系统的总结一下,一个很好的方案就是在Xmind中画一个草图,方便自己记忆和理解。
就拿今天咱们研究的这个http来说,可以带着如下几个问题去探索一下:
- http到底是什么。
- Request由几个部分组成,每部分都有什么作用,Respose呢?
- 你了解Respose的返回码,通常都表示什么?
HTTP 的定义
http给咱们最直观的印象就是在浏览器的地址栏中输入一个网址,然后点击回车就跳转到了一个新的网页。
还有就是对于咱们android开发来说,在自己的app中发送了一个网络请求然后获取到响应内容。
HTTP的定义
Hypertext Transfer Protocol,超⽂本传输协议,和 HTML (Hypertext Markup Language 超⽂本标记语⾔言) ⼀一起诞生,用于在⽹络上请求和传输 HTML 内容。
超文本,即「扩展型文本」,指的是 HTML 中可以有链向别的⽂本的链接 (hyperlink)。超链接和超文本是一个意思。
协议的意思又是啥呢
其实协议就是通信双方约定好的,必须共同遵从的一组规则。如,怎样建立链接,怎样相互识别。
html相信大家都不陌生,这里贴个图,不过多讲解了。
1. HTTP 的工作方式
1.1 浏览器:
用户输入地址后回车或点击链接 ->
浏览器拼装 HTTP 报文并发送请求给服务器 ->
服务器处理理请求后发送响应报文给浏览器 ->
浏览器解析响应报文并使用渲染引擎显示 到界⾯面
1.2 手机 App:
用户点击或界⾯面⾃自动触发联⽹网需求 ->
Android 代码调⽤拼装 HTTP 报文并发送请求到服务器 ->
服务器处理请求后发送响应报⽂给手机 ->
Android 代码处理响应报文并作出相应处理(如储存数据、加工数据、显示数据到界⾯面)
2. URL 和 HTTP 报文
2.1 URL 格式
URL由三部分组成,包括
- 协议类型
- 服务器地址(和端口号)
- 路径(Path)
协议类型:// 服务器地址[:端⼝号] 路径
具体格式
http://hencoder.com/users?gender=male
分别对应上面的协议类型、服务器地址和路径
2.2 报文格式
请求报⽂
格式如下图:
包括:请求行、Headers、Body(请求体,不是必须的)
- 请求行:图中的 GET 对应的是请求方式,/users 表示的的要访问的路径,和Host:api.github.com 不同。HTTP/1.1 表示的是当前 http 协议对应的版本,目前大多数浏览器使用的是 1.1版本,很多已经开始向2.0发展。
- Headers:下面会详细讲。
- Body:请求体,不是必须的,可以放一些请求的参数。用来存放服务器要处理的数据,而不是定位的数据。
响应报文
格式如下图:
包括:状态行、Headers、Body
- 状态行:包括 http 的使用版本,状态码和返回的状态信息。
- Headers
- Body:返回的信息放在 body 里面。
3. Request Method 请求方法
3.1 Get方式
http 0.9的唯一的一个请求方式。
- 用于获取资源
- 对服务器数据不会进行修改
- 不发送Body
GET /users/1 HTTP/1.1
Host: api.github.com
对应 Retrofit 的代码
@GET("/users/{id}")
Call<User> getUser(@Path("id") String id, @Query("gender") String gender);
3.2 Post方式
- 用于增加或者修改资源
- 发送给服务器的内容写在Body里面
POST /users HTTP/1.1
Host: api.github.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
name=rengwuxian&gender=male
对应 Retrofit 的代码
@FormUrlEncoded
@POST("/users")
Call<User> addUser(@Field("name") String name, @Field("gender") String gender);
3.3 Put方式
- 用于修改资源
- 发送给服务器的内容写在Body里面
- 在修改功能上和 post 的区别在于 put 是幂等操作(幂等:多次调用和一次调用的结果相同,比如说修改一个用户的性别为女,改一次是女,改多次还是女,所以 Get 也是幂等)。
PUT /users/1 HTTP/1.1
Host: api.github.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
gender=female
对应 Retrofit 的代码
@FormUrlEncoded
@PUT("/users/{id}")
Call<User> updateUser(@Path("id") String id, @Field("gender") String gender);
3.4 Delete方式
- 用于删除资源
- 不发送Body
DELETE /users/1 HTTP/1.1
Host: api.github.com
@DELETE("/users/{id}")
Call<User> getUser(@Path("id") String id, @Query("gender") String gender);
3.5 Head方式
- 和 Get 使用方法完全相同
- 和 Get 唯一区别在于,返回的响应中没有 Body
这个不太常见,没有 Body 体,一般用在下载文件的时候先进行一次获取文件大小的操作,由于没有响应没有 Body,数据可以放在 Headers 里面。
4. Status Code 状态码
状态码是一个由三位数字组成的一个数据序列,主要用来对结果做出类型化描述。
如:「获取成功」、「内容未找到」。
基本内容如下表
编码 | 表示内容 | 举例 |
---|---|---|
1xx | 临时性消息。 | 如:100 (数据过大,分批发送时:继续发送,对应的Header:Expect:100-continue)、101(正在切换协议) |
2xx | 成功。 | 最典型的是 200(OK)、201(创建成功)。 |
3xx | 重定向。 | 如 301(永久迁移)、302(暂时迁移)、304(内容未改变)。 |
4xx | 客户端错误。 | 如 400(客户端请求错误,如,请求了一个不存在的页面)、401(认证失败)、403(被禁止)、404(找不不到内容)。 |
5xx | 服务器器错误。 | 如 500(服务器器内部错误)。 |
最常用是2xx系列。所有2xx系列代表的都是成功。
然后是4xx系列,表示客户端错误。
5xx表示的表示服务器端出错了,方便程序员调试。
3xx系列表示重定向
301 永久迁移:如 http 转 https,浏览器直接做跳转。
302 暂时迁移:会保持权重,稍后可能会迁过来了
1xx系列:临时性消息
101(正在切换协议)比如从http 1.1升级到 2.0(Upgrade:h2c)。
100 (数据过大,分批发送时:继续发送,对应的Header:Expect:100-continue)
5. Header 首部
作用:HTTP 消息的 metadata。什么是metadata,就是元数据,通俗来讲就是关于数据的数据或者说数数据内部的数据,对核心数据起到辅助作用,比如说这个数据有多长,是什么格式…
5.1 Host
目标主机。注意:不是在网络上用于寻址的,而是在目标服务器上用于定位子服务器的。
一个IP可以对应多个域名,然后主主机再根据 Host 把数据转到子主机上。
5.2 Content-Type
指定 Body 的类型。主要有四类:
1. text/html
请求 Web 页面是返回响应的类型,Body 中返回 html 文本。格式如下:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 853
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
......
2. x-www-form-urlencoded
普通表单,Web 页面纯文本表单的提交方式,encoded URL 格式。
格式如下
POST /users HTTP/1.1
Host: api.github.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
name=rengwuxian&gender=male
对应 Retrofit 的代码:FormUrlEncoded 表示普通表单,用 Field 拼接参数。
@FormUrlEncoded
//用url拼成字符串放到body里面
@POST("/users")
Call<User> addUser(@Field("name") String name, @Field("gender") String gender);
3. multipart/form-data
Web 页面含有二进制文件时的提交方式。
格式如下:boundary 是用来做分界的,不同的部分用 boundary 来做显示的划分。以“–”加上 boundary 来做分割,最后一个 boundary 的末尾加上“–”。普通表单可以直接使用一个符号来做分割,multipart 可以传二进制格式所以不能用一个特定的符号来做分割,否则会造成混淆。
POST /users HTTP/1.1
Host: hencoder.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Length: 2382
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="name"
rengwuxian
------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="avatar"; filename="avatar.jpg"
Content-Type: image/jpeg
JFIFHHvOwX9jximQrWa......
------WebKitFormBoundary7MA4YWxkTrZu0gW--
对应 Retrofit 的代码:
@Multipart
@POST("/users")
Call<User> addUser(@Part("name") RequestBody name, @Part("gender") RequestBody avatar);
//使用
RequestBody namePart = RequestBody.create(MediaType.parse("text/plain"), nameStr);
RequestBody avatarPart = RequestBody.create(MediaType.parse("image/jpeg"), avatarFile);
api.addUser(namePart,avatarPart);
4. application/json , image/jpeg , application/zip …
单项内容(文本或非文本都可以),用于 Web Api 的响应或者 POST / PUT 的请求
application/json:json格式,用于 Web Api 的响应或 POST/PUT 请求。
请求中提交 JSON
POST /users HTTP/1.1
Host: hencoder.com
Content-Type: application/json; charset=utf-8
Content-Length: 38
{"name":"rengwuxian","gender":"male"}
对应 Retrofit 的代码:
@POST("/users")
Call<User> addUser(@Body("user") User user);
...
//使用 需要使用 Json 相关的 Converter
api.addUser(user);
响应中返回 JSON
HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
content-length: 234
[{"login":"mojombo","id":1,"node_id":"MDQ6VXNl
cjE=","avatar_url":"https://avatars0.githubuse
rcontent.com/u/1?v=4","gravat......
image/jpeg:上传图片和下载图片。
请求中提交二进制内容
POST /user/1/avatar HTTP/1.1
Host: hencoder.com
Content-Type: image/jpeg
Content-Length: 1575
JFIFHH9......
对应 Retrofit 的代码:
@POST("users/{id}/avatar")
Call<User> updateAvatar(@Path("id") String id, @Body RequestBody avatar);
...
RequestBody avatarBody = RequestBody.create(MediaType.parse("image/jpeg"),avatarFile);
api.updateAvatar(id, avatarBody)
响应中返回二进制内容
HTTP/1.1 200 OK
content-type: image/jpeg
content-length: 1575
JFIFHH9......
5.2 Content-Length
指定 Body 的长度(字节)。
5.3 Transfer: chunked (分块传输编码 Chunked Transfer Encoding)
用于当响应发起时,Body 长度还没能确定的情况下,服务端告诉客户端,我的数据要分块传输。和 Content-Length 不同时使用。它的用途是尽早给出响应,减少用户等待。
Body 的格式:最后传输 0 表示内容结束
<length1>
<data1>
<length2>
<data2>
0
具体格式:
HTTP/1.1 200 OK
Content-Type: text/html
Transfer-Encoding: chunked
4
Chun
9
ked Trans
12
fer Encoding
0
5.4 Location
指定重定向的目标 URL。在访问 http://hencoder.com 的时候,可以在控制台中 Status Code 为 3xx 的日志看到 Location。
5.5 User-Agent
用户代理,即是谁实际发送请求、接受响应的,例如手机浏览器、某款手机 App。
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
5.6 Range / Accept-Range
表示可以按范围取数据
Accept-Range: bytes 响应报文中出现,表示服务器⽀持按字节来取范围数据
Range: bytes=- 请求报文中出现,表示要取哪段数据
Content-Range:-/total 响应报⽂中出现,表示发送的是哪段数据
作用:
断点续传、多线程下载。
5.7 其他 Headers
- Accept: 客户端能接受的数据类型。如 text/html
- Accept-Charset: 客户端接受的字符集。如 utf-8
- Accept-Encoding: 客户端接受的压缩编码类型。如 gzip
- Content-Encoding:压缩类型。如 gzip
5.8 Cache
cache和buffer的区别
- cache:缓存,我这会用完了可能还要用,先存一下备用
- buffer:缓冲,上游多生产了一些给下游稍后用。
Cache-Control:
no-cache、no-store、max-age
- no-cache:不是不能缓存,下次请求时要先询问缓存是否有效。如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,但如果资源未发生变化,则可避免下载
- no-store:禁止浏览器以及所有中间缓存存储任何版本的返回响应。
- private:私有缓存,这里不做过多解释。
- public:公有缓存,这里不做过多解释。
作用:
在客户端或中间网络节点缓存数据,降低从服务器取数据的频率,以提高网络性能。
6. REST
REST 的定义众说纷纭,没有统一答案。
扔物线的观点:REST HTTP 即正确使用 HTTP。包括: 使用资源的格式来定义 URL
完结。
感谢朱凯老师提供的视频教学。