Skip to content

Latest commit

 

History

History
213 lines (148 loc) · 10.8 KB

HTTP前后端二三事.md

File metadata and controls

213 lines (148 loc) · 10.8 KB

HTTP 后端二三事

最近写了一个项目,对于 HTTP 的前后端增进了一篇了解,所以写一篇文章记录一下。

HTTP 协议

http 协议是目前应用最广泛的网络协议,构建于底层的 TCP 协议之上。

http 协议是一个基于 文本 1应用层 协议。而传统上口头的“HTTP”一般并不狭义的指代这个协议本身,还包括了与之相关的一系列协议、软件等等。

文本

这说明 http 协议传输的内容是可以被很容易的解析的,其底层并不复杂。在很多单片机的实现之中,http 包的收发是直接手工一行行书写 printf() 的。

应用层

这说明 http 协议是一个顶层协议,与之同级的包括以下这些,希望你听说过以下的一部分或全部:

  • ftp 协议,用于进行文件传输
  • ssh 协议,一般用于远程连接
  • jdbc 协议,用于数据库连接
  • DNS 协议,用于 DNS 服务
  • SMTP/POP 协议,用于邮件服务
  • https 协议,是 http 的一个拓展,通过在 TCP 协议层上插入了一层 SSL/TLS 协议来保证安全性。

一般来说,这些应用层协议可以通过 {协议名}://xxx 的 url 来获取资源。现在我们先明确,一个 http url 的格式长这样:

”http://[user]:[password]@{主机名}[:端口]{/路径}?[查询参数]#[片段]“

http 规定,不填写端口号的情况下使用 80 端口。

关于「主机」和「端口」是什么,我们留到 TCP 协议的时候再讲(挖坑233

「路径」和 「查询参数」,我们接下来讲。

「片段」不会实际发动到服务器,而是告诉浏览器在获取到资源之后直接跳转到这个位置。

HTTP 实现

我们说,http 是一个协议,意思是说它只是一个『纸面约定』,没有规定任何实现。

首先我们要知道的是,http 协议包括两种,请求(request)和响应(response),有一些特性指引在请求包里应用,有些只能在响应包里应用,但大部分是通用的。

一般来说,不同的语言都要自己完成对于 http 协议的实现,例如 Go 的 net/http,Python 的 urllib3,等等。这些都是客户端,可以发送请求处理响应

一些软件也提供了 http 协议的实现,例如 Apache/Nginx,基于 Apache 的 Tomcat 等。一般其性能较高,且包括了许多实用工具,如反向代理等。这些软件是服务端,可以接受请求返回响应

协议内容构成

很多开发者都知道,http 有 GET、POST 等几种方法,但是对于具体是怎样的,并不明确。

http 传输的一个包有以下内容:

  • 请求头/响应头
    • 起始行,单独一行,包含一些额外信息;
    • 头部(headers),包含了若干键值对,用于给后面的内容做说明;
  • 实体(body),也称请求体/响应体,一般是要传输的主要内容,但需要的情况下可以不传输。
    • body 译为实体并不广泛接受,下文提及时我会一律使用 “实体(body)”来表述这个东西,以免产生歧义。
    • 实体(body)的内容不一而足,不可能也没有必要一一介绍,后文会介绍一些对于实体(body)描述/给出限制的方法。

起始行

请求和响应的起始行格式不同,但都是一行里三个用空格分开的词,分别举例如下:

GET /api/nht/blog/example HTTP/1.1

<方法> <路径> <版本>

  • 关于「方法」的信息,下面会讲;
  • 这里的「路径」包括了 url 中的「路径」和「查询参数」几个部分。
  • 「版本」 90% 都是 “HTTP/1.1”,尤其是比较小的网站。
    • 如果是支持 http/2 的页面,那么就是“HTTP/2”

HTTP/1.1 200 OK

<版本> <状态码> <信息>

  • 「版本」和前面的说法一样;
  • 平时说的 “404 Not Found”,“403 Fobbiden” 之类的,就是这里的「状态码」和「信息」了。
    • 这两个信息用于给客户端提供信息。
    • 常见的状态码:
      • 2xx:请求成功
        • 200 OK,请求成功;
        • 204 No Content,请求成功,但是没有 body 部分
        • 206 Partial Content,请求成功,但是body内容只是一部分,要的话得继续请求
          • 一般用于分块传输的情况
      • 3xx:重定向
        • 301 Moved Permanently,永久重定向
        • 302 Found,临时重定向,一般例如主机维护等情况使用
      • 4xx:客户端错误
        • 400 Bad Request,没有说明错误类型
        • 403 Fobbiden,拒绝访问
        • 404 Not Found,找不到资源
      • 5xx 内部错误
        • 500 Internal,没有进一步说明
        • 502 Bad Gateway,代理错误

起始行用一个换行符(CRLF)结束。

头部

头部(headers)是若干个无序的键值对,每行一对。键(一般称为字段)和值之间用冒号分隔。

字段不区分大小写,不能含有空格或者下划线(建议用短横代替)。

除非语义允许(如设置 Cookie),否则键是不重复的。

http 规定了一些标准头(这里头指的是头部字段),但也允许传输更多自定义头,类似标准库和第三方库的关系。

下面介绍一些常用头。

  • 通用字段:没有限制;
    • Date:报文生成时间,一般出现在响应中。
    • Content-xxx,说明实体(body)的实际内容,下面和 Accept 一起细讲
      • 注意请求的时候也可以加这个字段,因为上传文件等信息是需要这些描述的
  • 请求字段:只会出现在请求头中;
    • Host,说明请求发送到何方,必填。
    • User-Agent,说明客户端使用何种浏览器(君子协议),一般称 UA。
    • Cookie,说明客户端使用的 cookie 内容,用分号连接多个。
    • Accept-xxx,说明客户端接受的内容,下面细讲
  • 响应字段:只会出现在响应头中;
    • Set-Cookie,设置 Cookie
  • 实体(body)字段:用于描述实体(body)的信息。
    • Content-Length,描述实体(body)长度,如果没有说明长度不定,需要分块传输。

Accept 和 Content

这个系列是重要的控制和描述实体(body)内容的头部信息,成对出现,包括:

  • Accept/Content-Type:MIME,标记资源类型
    • MIME 是一个标记类型的系统,最早用于电子邮件,格式为 {大类型}/{小类型}。例如:
      • text/html,text/css
      • image/gif,image/jpeg,image/png
      • audio/mpeg、video/mp4
      • application/json,application/javascript、application/pdf
  • Content-Encoding:none|gzip|br,标记返回资源压缩方法
  • Accept-Language/Content-Language,标记客户端接受的语言种类。
    • zh-CN, zh, en,etc

HTTP 请求方法

只讲几种常用的,有些不常用的不讲了。

  • HEAD:只获取头部,不需要实体(body)
    • 对于实体(body),http 协议没有做过多的控制
    • 头部携带了大量描述信息,是 http 的精华和重中之重。
    • 因此,专门提供一个请求方法来获取头部,是有必要的。
    • 例如获取 cookie 等情况即可使用此方法
  • GET:获取资源,可以理解为读取或者下载数据
    • 不包含实体(body)
    • 是 http 最古老的方法,可以追溯到远古的 http/0.9 还没有彻底成型的版本
  • DELETE:删除资源
  • POST:向资源提交数据,相当于写入或上传数据
    • 可以包含实体(body)
  • PUT:类似 POST,RESTful 架构建议用这个方法来更新资源。
    • 可以包含实体(body)
  • OPTIONS:列出可对资源实行的方法

可以注意到 DELETE POST GET PUT 分别对应数据库的增删查改,当然这只是对应,http 并不禁止你所有操作都通过 POST,甚至都通过 UNLINK(http/2 定义的新方法,语义为断开链接)进行。

HTTP 状态维持

HTTP 设计的时候是无状态(statusless)的,服务器没有记忆能力。

使用 Cookie 技术来提供历史信息,cookie 由用户的浏览器储存和维护,实际传输的时候通过头部传输。参见前文中请求和响应的 Cookie/set-Cookie 字段。

cookie 是明文的,不能存储用户密码等信息,这时候就要在服务器保存一些信息。这些信息称为「session」。但由于服务器不能分辨用户,所以 session 的应用还是基于 cookie 的,一般通过生成一个口令的方式完成。

如何让从请求中获取信息

对于后端开发来说,如果不明了如何从请求中获取信息,编码过程就变成了依靠机械记忆的工作,因此有必要对这个部分进行总结。

  • 查询参数(query),由请求的起始行第二个路径参数解析提供;
    • 这是最常用的参数,RESTful 建议每个获取数组的请求都在这里添加参数;
    • 包括单页限制,单页偏移,排序字段,升序/降序。
  • 路径(path),由请求的起始行第二个路径参数解析提供;
    • 狭义,指的是 url 的路径,由于前后分离,路径被绑定给某个函数处理,因而可以包含信息。
  • 请求体(body),这要求请求方法不能是 GET;
    • 尽管相当一部分时候根本没有请求体,但是如果有了,请求体肯定是不可或缺的。
  • cookie,由请求头中的 Cookie 字段获取;
    • 这是保持连接状态的重要手段。
  • 请求头(header),由请求头部本身获取。
    • 这个相对不常用一些。

HTTPS

https 是一个基于 http 的协议,添加了 ssl/tls 协议层来保证安全,默认端口号为 443,其他和HTTP/1.1 没有区别。主要解决了 http 明文传输,可能会遇到中间人攻击(被传输路径中间上的某一部篡改内容)的问题

首先明确一下,和 TCP/IP 的情况不同, SSL/TLS 并不是两个协议,自始至终只有一个协议,只是它原来叫 SSL,后来改名叫 TLS 了(有一些不本质的变化,影响不大)。

基本上来说,这个协议干了两件事情:

  1. 使用公钥加密,保证传输数据没有经过中间人篡改;
  2. 通过第三方的数字证书,保证你获取到的公钥本身没有经过篡改。
    • 否则的话,攻击者用自己的公钥替换服务器给的,通信双方还是不能建立互信;
    • 第三方认证机构(Certification Authority,CA)的公钥一般内嵌在操作系统/浏览器中。

HTTP/2

在语义上,2 和 1 是完全兼容的。但在语法上给出了一些重大变化:

  • 使用二进制格式传输;
  • 允许头部压缩;
  • 服务器主动推送数据,不必等待客户端浏览器解析请求。

参考

[](https://developer.mozilla.org/zh-CN/docs/Web/HTTP

Footnotes

  1. 指目前通用流行的 http/1.1;http/2 使用二进制编码,但是现在应用很少所以本文不考虑。