3.1.1 HTTP 协议基础

3.1.1 HTTP 协议基础 #

HTTP(HyperText Transfer Protocol)是互联网上应用最广泛的网络协议之一。作为一个应用层协议,HTTP 定义了客户端和服务器之间如何进行通信。深入理解 HTTP 协议是进行 Web 开发的基础,本节将详细介绍 HTTP 协议的核心概念和工作机制。

HTTP 协议概述 #

协议特点 #

HTTP 协议具有以下主要特点:

  1. 无状态性:每个请求都是独立的,服务器不会保留之前请求的信息
  2. 请求-响应模型:客户端发送请求,服务器返回响应
  3. 基于 TCP:HTTP 建立在可靠的 TCP 连接之上
  4. 文本协议:HTTP 消息使用人类可读的文本格式
  5. 可扩展性:通过头部字段可以轻松扩展功能

协议版本 #

HTTP 协议经历了多个版本的演进:

  • HTTP/0.9(1991 年):最初版本,只支持 GET 方法
  • HTTP/1.0(1996 年):引入了头部字段、状态码、多种请求方法
  • HTTP/1.1(1997 年):持久连接、分块传输、缓存控制
  • HTTP/2(2015 年):二进制协议、多路复用、服务器推送
  • HTTP/3(2022 年):基于 QUIC 协议,进一步提升性能

HTTP 消息结构 #

请求消息结构 #

HTTP 请求消息由以下部分组成:

请求行
请求头部
空行
请求体(可选)

请求行 #

请求行包含三个部分:

方法 URI HTTP版本

示例:

GET /api/users HTTP/1.1
POST /api/users HTTP/1.1
PUT /api/users/123 HTTP/1.1

请求头部 #

请求头部包含多个键值对,提供请求的附加信息:

Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

完整请求示例 #

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 45
Authorization: Bearer token123

{"name": "张三", "email": "[email protected]"}

响应消息结构 #

HTTP 响应消息结构如下:

状态行
响应头部
空行
响应体(可选)

状态行 #

状态行包含 HTTP 版本、状态码和状态描述:

HTTP版本 状态码 状态描述

示例:

HTTP/1.1 200 OK
HTTP/1.1 404 Not Found
HTTP/1.1 500 Internal Server Error

完整响应示例 #

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 78
Date: Mon, 23 Oct 2023 10:30:00 GMT

{"id": 123, "name": "张三", "email": "[email protected]", "created_at": "2023-10-23T10:30:00Z"}

HTTP 请求方法 #

HTTP 定义了多种请求方法,每种方法都有特定的语义:

GET 方法 #

用于获取资源,是最常用的方法:

GET /api/users HTTP/1.1
Host: api.example.com

特点:

  • 安全的(不会修改服务器状态)
  • 幂等的(多次执行结果相同)
  • 可缓存
  • 参数通过 URL 查询字符串传递

POST 方法 #

用于创建资源或提交数据:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json

{"name": "李四", "email": "[email protected]"}

特点:

  • 不安全(会修改服务器状态)
  • 非幂等(多次执行可能产生不同结果)
  • 不可缓存
  • 数据在请求体中传递

PUT 方法 #

用于更新或创建资源:

PUT /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json

{"name": "李四", "email": "[email protected]"}

特点:

  • 不安全
  • 幂等的
  • 通常用于完整更新资源

DELETE 方法 #

用于删除资源:

DELETE /api/users/123 HTTP/1.1
Host: api.example.com

特点:

  • 不安全
  • 幂等的
  • 用于删除指定资源

PATCH 方法 #

用于部分更新资源:

PATCH /api/users/123 HTTP/1.1
Host: api.example.com
Content-Type: application/json

{"email": "[email protected]"}

HEAD 方法 #

类似 GET,但只返回头部信息:

HEAD /api/users/123 HTTP/1.1
Host: api.example.com

OPTIONS 方法 #

用于获取服务器支持的方法:

OPTIONS /api/users HTTP/1.1
Host: api.example.com

HTTP 状态码 #

HTTP 状态码用于表示请求的处理结果,分为五个类别:

1xx 信息性状态码 #

  • 100 Continue:客户端应继续发送请求
  • 101 Switching Protocols:服务器正在切换协议

2xx 成功状态码 #

  • 200 OK:请求成功
  • 201 Created:资源创建成功
  • 202 Accepted:请求已接受,但尚未处理
  • 204 No Content:请求成功,但无内容返回

3xx 重定向状态码 #

  • 301 Moved Permanently:资源永久移动
  • 302 Found:资源临时移动
  • 304 Not Modified:资源未修改,可使用缓存

4xx 客户端错误状态码 #

  • 400 Bad Request:请求格式错误
  • 401 Unauthorized:未授权
  • 403 Forbidden:禁止访问
  • 404 Not Found:资源不存在
  • 405 Method Not Allowed:方法不允许
  • 409 Conflict:请求冲突
  • 422 Unprocessable Entity:请求格式正确但语义错误

5xx 服务器错误状态码 #

  • 500 Internal Server Error:服务器内部错误
  • 501 Not Implemented:功能未实现
  • 502 Bad Gateway:网关错误
  • 503 Service Unavailable:服务不可用
  • 504 Gateway Timeout:网关超时

HTTP 头部字段 #

HTTP 头部字段提供了请求和响应的元数据信息。

通用头部字段 #

这些字段可以出现在请求和响应中:

Date: Mon, 23 Oct 2023 10:30:00 GMT
Connection: keep-alive
Cache-Control: no-cache
Transfer-Encoding: chunked

请求头部字段 #

客户端发送的头部字段:

Host: api.example.com
User-Agent: Go-http-client/1.1
Accept: application/json, text/plain
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Cookie: session_id=abc123; user_pref=dark_mode
If-Modified-Since: Mon, 23 Oct 2023 09:00:00 GMT
If-None-Match: "686897696a7c876b7e"

响应头部字段 #

服务器返回的头部字段:

Server: nginx/1.18.0
Content-Type: application/json; charset=utf-8
Content-Length: 1234
Content-Encoding: gzip
Set-Cookie: session_id=xyz789; Path=/; HttpOnly; Secure
Location: /api/users/124
ETag: "686897696a7c876b7e"
Last-Modified: Mon, 23 Oct 2023 10:30:00 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE

实体头部字段 #

描述消息体的字段:

Content-Type: application/json; charset=utf-8
Content-Length: 1234
Content-Encoding: gzip
Content-Language: zh-CN
Content-Range: bytes 200-1023/1024

内容协商 #

HTTP 支持内容协商,允许客户端和服务器协商最适合的内容格式。

Accept 系列头部 #

客户端使用 Accept 系列头部表明偏好:

Accept: application/json, application/xml;q=0.9, text/plain;q=0.8
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Charset: utf-8, iso-8859-1;q=0.5

质量值(q 值) #

q 值表示偏好程度,范围从 0 到 1:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

服务器响应 #

服务器根据客户端偏好返回合适的内容:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Language: zh-CN
Content-Encoding: gzip
Vary: Accept, Accept-Language, Accept-Encoding

缓存机制 #

HTTP 缓存机制可以显著提升性能和用户体验。

缓存控制头部 #

Cache-Control: public, max-age=3600
Cache-Control: private, no-cache
Cache-Control: no-store
Expires: Mon, 23 Oct 2023 11:30:00 GMT

条件请求 #

客户端可以发送条件请求来验证缓存:

If-Modified-Since: Mon, 23 Oct 2023 09:00:00 GMT
If-None-Match: "686897696a7c876b7e"

服务器响应:

HTTP/1.1 304 Not Modified
ETag: "686897696a7c876b7e"
Cache-Control: public, max-age=3600

持久连接 #

HTTP/1.1 默认使用持久连接,可以在同一个 TCP 连接上发送多个请求。

Connection 头部 #

Connection: keep-alive
Connection: close

Keep-Alive 头部 #

Keep-Alive: timeout=5, max=100

分块传输编码 #

当内容长度未知时,可以使用分块传输编码:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/plain

7\r\n
Mozilla\r\n
9\r\n
Developer\r\n
7\r\n
Network\r\n
0\r\n
\r\n

HTTPS 和安全性 #

HTTPS 是 HTTP 的安全版本,使用 TLS/SSL 加密:

TLS 握手过程 #

  1. 客户端发送 Client Hello
  2. 服务器发送 Server Hello 和证书
  3. 客户端验证证书
  4. 密钥交换和加密建立
  5. 开始加密通信

安全头部 #

Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block

HTTP/2 特性 #

HTTP/2 引入了多项性能改进:

二进制协议 #

HTTP/2 使用二进制格式而非文本格式,提高了解析效率。

多路复用 #

在单个连接上可以并行发送多个请求和响应:

// HTTP/1.1 需要多个连接
conn1 -> GET /api/users
conn2 -> GET /api/posts
conn3 -> GET /api/comments

// HTTP/2 可以在单个连接上复用
conn -> GET /api/users (stream 1)
     -> GET /api/posts (stream 3)
     -> GET /api/comments (stream 5)

服务器推送 #

服务器可以主动推送资源给客户端:

PUSH_PROMISE frame:
:method: GET
:path: /style.css
:scheme: https
:authority: example.com

头部压缩 #

使用 HPACK 算法压缩头部,减少传输开销。

实际应用示例 #

让我们通过一个完整的 HTTP 交互示例来理解协议的工作流程:

用户注册流程 #

  1. 客户端发送注册请求
POST /api/register HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 67
User-Agent: MyApp/1.0

{"username": "zhangsan", "email": "[email protected]", "password": "secret123"}
  1. 服务器处理并响应
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 156
Location: /api/users/12345
Set-Cookie: session_id=abc123xyz; Path=/; HttpOnly; Secure; SameSite=Strict
Date: Mon, 23 Oct 2023 10:30:00 GMT

{"id": 12345, "username": "zhangsan", "email": "[email protected]", "created_at": "2023-10-23T10:30:00Z"}
  1. 客户端获取用户信息
GET /api/users/12345 HTTP/1.1
Host: api.example.com
Cookie: session_id=abc123xyz
Accept: application/json
If-None-Match: "v1.0"
  1. 服务器返回用户信息
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 156
ETag: "v1.0"
Cache-Control: private, max-age=300
Last-Modified: Mon, 23 Oct 2023 10:30:00 GMT

{"id": 12345, "username": "zhangsan", "email": "[email protected]", "created_at": "2023-10-23T10:30:00Z"}

最佳实践 #

1. 正确使用 HTTP 方法 #

GET /api/users          # 获取用户列表
POST /api/users         # 创建新用户
GET /api/users/123      # 获取特定用户
PUT /api/users/123      # 完整更新用户
PATCH /api/users/123    # 部分更新用户
DELETE /api/users/123   # 删除用户

2. 合理设置状态码 #

// 成功创建资源
w.WriteHeader(http.StatusCreated) // 201

// 资源未找到
w.WriteHeader(http.StatusNotFound) // 404

// 请求参数错误
w.WriteHeader(http.StatusBadRequest) // 400

// 服务器内部错误
w.WriteHeader(http.StatusInternalServerError) // 500

3. 设置适当的头部 #

Content-Type: application/json; charset=utf-8
Cache-Control: public, max-age=3600
ETag: "version-1.0"
Last-Modified: Mon, 23 Oct 2023 10:30:00 GMT

4. 实现内容协商 #

accept := r.Header.Get("Accept")
switch {
case strings.Contains(accept, "application/json"):
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(data)
case strings.Contains(accept, "application/xml"):
    w.Header().Set("Content-Type", "application/xml")
    xml.NewEncoder(w).Encode(data)
default:
    w.Header().Set("Content-Type", "text/plain")
    fmt.Fprint(w, data)
}

小结 #

HTTP 协议是 Web 开发的基础,理解其工作原理对于构建高质量的 Web 应用至关重要。本节介绍了:

  1. 协议特点:无状态、请求-响应模型、基于 TCP
  2. 消息结构:请求行/状态行、头部、消息体
  3. 请求方法:GET、POST、PUT、DELETE 等的语义和用法
  4. 状态码:1xx-5xx 各类状态码的含义
  5. 头部字段:通用、请求、响应、实体头部的作用
  6. 高级特性:内容协商、缓存、持久连接、HTTP/2

掌握这些基础知识将为后续学习 Go HTTP 编程奠定坚实的基础。在下一节中,我们将深入学习 Go 语言的 net/http 包,了解如何在实际开发中应用这些 HTTP 协议知识。