服务端解析 HTTP 请求数据,是开发 Web 服务的基础。不同编程语言和框架实现方式不同,但其底层原理基本相同,主要是对客户端发来的HTTP 报文进行解析,提取出其中的请求行、请求头、请求体等内容。
下面我将从通用的 HTTP 请求结构出发,介绍服务端如何一步步解析 HTTP 请求,包括:
一、HTTP 请求报文结构(重点)
一个完整的 HTTP 请求报文由三部分组成:
1. 请求行 (Request Line)
2. 请求头 (Request Headers)
3. 请求体 (Request Body) —— 可选
示例:一个常见的 HTTP POST 请求
POST /submit-form HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
User-Agent: curl/7.68.0
name=Tom&age=18&city=Beijing
二、服务端解析流程概述
步骤 1:接收 TCP 数据流
HTTP 协议是基于 TCP 协议的,服务端首先监听端口(如 80、443),接收客户端发来的原始字节流。
-
对于原始数据:
b"POST /submit-form HTTP/1.1\r\nHost: www.example.com\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 27\r\n\r\nname=Tom&age=18&city=Beijing"
步骤 2:解析请求行
POST /submit-form HTTP/1.1
将其按空格 拆分成 3 个部分:
-
方法:
POST -
路径(资源 URI):
/submit-form -
协议版本:
HTTP/1.1
步骤 3:解析请求头(Headers)
请求头部分每一行是一个键值对,格式为:Header-Name: Header-Value
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
User-Agent: curl/7.68.0
解析方式通常是:
-
按
\r\n分行; -
每行按冒号
:分割成 key-value; -
存储为 Map / Dict 结构。
步骤 4:根据 Content-Length 读取请求体(Body)
请求体存在的条件:
-
请求方法为
POST/PUT/PATCH -
请求头中存在
Content-Length或Transfer-Encoding: chunked
示例请求体:
name=Tom&age=18&city=Beijing
根据请求头中的 Content-Type 决定如何解析请求体:
| Content-Type | 解析方式说明 |
|---|---|
application/x-www-form-urlencoded | 表单格式,使用 key1=value1&key2=value2 解析 |
application/json | JSON 格式,可以用 JSON.parse() 解析 |
multipart/form-data | 文件上传格式,需要边界字符串做切割 |
三、不同语言下的解析示例
1. Python(使用 BaseHTTPRequestHandler)
from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib.parse
class MyHandler(BaseHTTPRequestHandler):
def do_POST(self):
content_len = int(self.headers['Content-Length'])
body = self.rfile.read(content_len)
parsed = urllib.parse.parse_qs(body.decode())
print("方法:", self.command)
print("路径:", self.path)
print("头部:", dict(self.headers))
print("请求体:", parsed)
2. Java(Servlet 示例)
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
String age = request.getParameter("age");
BufferedReader reader = request.getReader();
String body = reader.lines().collect(Collectors.joining());
System.out.println("请求方法: " + request.getMethod());
System.out.println("路径: " + request.getRequestURI());
System.out.println("请求体: " + body);
}
3. Node.js(原生 http 模块)
const http = require('http');
http.createServer((req, res) => {
let body = '';
req.on('data', chunk => {
body += chunk;
});
req.on('end', () => {
console.log('方法:', req.method);
console.log('路径:', req.url);
console.log('头部:', req.headers);
console.log('请求体:', body);
});
}).listen(8080);
四、注意事项
-
编码问题:请求体通常为 UTF-8 编码,但也可能为 GBK 等,需根据
Content-Type判断; -
数据大小控制:需限制请求体大小,防止被攻击(DoS);
-
文件上传处理复杂:
multipart/form-data格式解析需专门库支持; -
安全性:避免直接拼接请求内容到数据库等操作,防止注入攻击。
五、总结流程图
TCP连接 ->
读取原始数据 ->
解析请求行 ->
解析请求头 ->
判断并读取请求体 ->
根据Content-Type解析内容 ->
处理业务逻辑
1万+

被折叠的 条评论
为什么被折叠?



