前言
大家好,我是 倔强青铜三 。欢迎关注我,微信公众号: 倔强青铜三。点赞、收藏、关注,一键三连!
欢迎来到 苦练Python第 71 天!
今天我们来学习python标准库中的http.server库。
想用Python快速搭建一个HTTP服务器?
不用装Flask、不用搞Django,更不用碰Nginx,Python自带的http.server模块就能帮你搞定!
它虽然不是为生产环境准备的"重型武器",但在本地开发、接口调试、临时文件共享等场景下,简直不要太方便。
一、基础概念
1.1 HTTP 协议 30 秒速记
- 请求/响应模型:客户端 → 请求 → 服务器 → 响应 → 客户端
- 常见方法:GET(读)、POST(写)、PUT、DELETE...
- 状态码:200 OK、404 Not Found、500 Internal Server Error...
1.2 Python http.server 的定位
官方文档一句话总结:
"A simple HTTP server for development and testing."
轻到没朋友,不适合生产,但本地调试、局域网文件分享爽到飞起。
二、核心类与模块结构
2.1 HTTPServer 类
位于 http.server.HTTPServer,只做三件事:
- 监听端口
- 接收连接
- 把连接丢给"请求处理器"
构造函数签名:
python
HTTPServer(server_address, RequestHandlerClass)
2.2 BaseHTTPRequestHandler 类
生命周期钩子:
setup()准备环境handle()真正干活(如do_GET())finish()清理现场
常用属性:
self.client_address客户端地址self.command请求方法self.path请求路径self.headers请求头(类似email.message.EmailMessage)
三、基础功能实现
3.1 单线程"Hello World"服务器
文件 hello.py:
python
from http.server import HTTPServer, BaseHTTPRequestHandler
class HelloHandler(BaseHTTPRequestHandler):
def do_GET(self):
body = b'Hello from http.server!\n'
self.send_response(200)
self.send_header('Content-Type', 'text/plain; charset=utf-8')
self.send_header('Content-Length', str(len(body)))
self.end_headers()
self.wfile.write(body)
if __name__ == '__main__':
server = HTTPServer(('localhost', 8000), HelloHandler)
print('Serving on http://localhost:8000/ (Ctrl+C to stop)')
server.serve_forever()
运行结果(终端):
vbnet
Serving on http://localhost:8000/ (Ctrl+C to stop)
127.0.0.1 - - [21/Aug/2025 15:42:03] "GET / HTTP/1.1" 200 -
浏览器访问效果:
页面显示 Hello from http.server!
3.2 解析 GET 路径与查询参数
文件 echo_get.py:
python
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
class EchoGetHandler(BaseHTTPRequestHandler):
def do_GET(self):
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
body = f'path={parsed.path}\nparams={params}\n'.encode()
self.send_response(200)
self.send_header('Content-Type', 'text/plain; charset=utf-8')
self.end_headers()
self.wfile.write(body)
if __name__ == '__main__':
HTTPServer(('localhost', 8001), EchoGetHandler).serve_forever()
运行结果(终端):
css
127.0.0.1 - - [21/Aug/2025 15:43:10] "GET /foo?a=1&b=2 HTTP/1.1" 200 -
浏览器访问 http://localhost:8001/foo?a=1&b=2 页面返回:
ini
path=/foo
params={'a': ['1'], 'b': ['2']}
3.3 处理 POST(表单 & JSON)
文件 echo_post.py:
python
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
class EchoPostHandler(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers.get('Content-Length', 0))
body_bytes = self.rfile.read(length)
try:
data = json.loads(body_bytes)
except Exception:
data = body_bytes.decode()
resp = json.dumps({'received': data}).encode()
self.send_response(200)
self.send_header('Content-Type', 'application/json; charset=utf-8')
self.end_headers()
self.wfile.write(resp)
if __name__ == '__main__':
HTTPServer(('localhost', 8002), EchoPostHandler).serve_forever()
运行结果(终端):
arduino
127.0.0.1 - - [21/Aug/2025 15:44:31] "POST / HTTP/1.1" 200 -
使用 curl 测试:
bash
curl -X POST http://localhost:8002/ -H "Content-Type: application/json" -d '{"msg":"hi"}'
返回:
json
{"received":{"msg":"hi"}}
四、静态文件服务
4.1 一行命令托管当前目录
终端直接敲:
bash
python -m http.server 8003
运行结果:
csharp
Serving HTTP on 0.0.0.0 port 8003 (http://0.0.0.0:8003/) ...
127.0.0.1 - - [21/Aug/2025 15:45:55] "GET / HTTP/1.1" 200 -
浏览器访问即可浏览目录或文件。
4.2 代码方式自定义根目录
文件 static_custom.py:
python
from http.server import HTTPServer, SimpleHTTPRequestHandler
import os
web_dir = os.path.join(os.path.dirname(__file__), 'public')
os.chdir(web_dir) # 切换工作目录
server = HTTPServer(('localhost', 8004), SimpleHTTPRequestHandler)
print(f'Serving files from {web_dir}')
server.serve_forever()
运行结果:
csharp
Serving files from /Users/xxx/demo/public
127.0.0.1 - - [21/Aug/2025 15:47:12] "GET /index.html HTTP/1.1" 200 -
五、多线程与性能优化
5.1 ThreadingHTTPServer 登场
文件 thread_server.py:
python
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
server = ThreadingHTTPServer(('localhost', 8005), SimpleHTTPRequestHandler)
print('Threading server on http://localhost:8005/')
server.serve_forever()
运行结果:
csharp
Threading server on http://localhost:8005/
127.0.0.1 - - [21/Aug/2025 15:48:22] "GET /large.zip HTTP/1.1" 200 -
多线程可同时处理多个下载,但仍是 I/O 阻塞,高并发请上专业 WSGI。
六、请求处理扩展
6.1 自定义 404 页面
文件 custom_404.py:
python
from http.server import HTTPServer, BaseHTTPRequestHandler
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.send_response(200)
self.end_headers()
self.wfile.write(b'Home sweet home')
else:
self.send_error(404, 'Custom 404: Nothing here')
server = HTTPServer(('localhost', 8006), MyHandler)
server.serve_forever()
运行结果(访问不存在的 /xyz):
arduino
127.0.0.1 - - [21/Aug/2025 15:49:30] "GET /xyz HTTP/1.1" 404 -
浏览器显示:
vbnet
Error 404: Custom 404: Nothing here
七、高级特性
7.1 极简路由系统
文件 mini_router.py:
python
import re, json
from http.server import HTTPServer, BaseHTTPRequestHandler
class RouterHandler(BaseHTTPRequestHandler):
routes = [
(re.compile(r'^/api/time$'), 'handle_time'),
(re.compile(r'^/api/add/(\d+)/(\d+)$'), 'handle_add'),
]
def do_GET(self):
for pattern, method in self.routes:
m = pattern.match(self.path)
if m:
getattr(self, method)(*m.groups())
return
self.send_error(404)
def handle_time(self):
import datetime
self.send_json({'time': str(datetime.datetime.now())})
def handle_add(self, a, b):
self.send_json({'sum': int(a) + int(b)})
def send_json(self, obj):
body = json.dumps(obj).encode()
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(body)
server = HTTPServer(('localhost', 8007), RouterHandler)
server.serve_forever()
运行结果:
css
127.0.0.1 - - [21/Aug/2025 15:51:00] "GET /api/time HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2025 15:51:05] "GET /api/add/3/4 HTTP/1.1" 200 -
浏览器访问 /api/add/3/4 返回:
json
{"sum":7}
八、安全与生产注意事项
- 默认无认证、无 HTTPS、无限制上传大小。
- 不要暴露在公网!
- 生产上请用 Nginx + Gunicorn/uWSGI + Flask/Django。
九、实战项目(可跑代码)
9.1 RESTful TODO API(极简版)
文件 todo_api.py(仅内存存储):
python
import json, uuid
from http.server import HTTPServer, BaseHTTPRequestHandler
todos = {}
class TodoHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_json(list(todos.values()))
def do_POST(self):
data = self.read_json()
tid = str(uuid.uuid4())
todos[tid] = {'id': tid, 'task': data['task'], 'done': False}
self.send_json(todos[tid], 201)
def do_DELETE(self):
tid = self.path.split('/')[-1]
todos.pop(tid, None)
self.send_response(204)
self.end_headers()
def read_json(self):
length = int(self.headers.get('Content-Length', 0))
return json.loads(self.rfile.read(length))
def send_json(self, obj, status=200):
body = json.dumps(obj).encode()
self.send_response(status)
self.send_header('Content-Type', 'application/json')
self.end_headers()
self.wfile.write(body)
server = HTTPServer(('localhost', 8008), TodoHandler)
print('TODO API on http://localhost:8008/')
server.serve_forever()
运行结果(终端):
csharp
TODO API on http://localhost:8008/
127.0.0.1 - - [21/Aug/2025 15:52:33] "POST / HTTP/1.1" 201 -
127.0.0.1 - - [21/Aug/2025 15:52:40] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2025 15:52:46] "DELETE /123... HTTP/1.1" 204 -
9.2 文件上传服务(multipart/form-data)
文件 upload_server.py:
python
import cgi, os, tempfile
from http.server import HTTPServer, BaseHTTPRequestHandler
class UploadHandler(BaseHTTPRequestHandler):
def do_GET(self):
html = b'''
<form method="POST" enctype="multipart/form-data">
<input name="file" type="file"/>
<input type="submit"/>
</form>
'''
self.send_response(200)
self.send_header('Content-Type', 'text/html; charset=utf-8')
self.end_headers()
self.wfile.write(html)
def do_POST(self):
form = cgi.FieldStorage(fp=self.rfile, headers=self.headers,
environ={'REQUEST_METHOD': 'POST'})
if 'file' in form and form['file'].filename:
with tempfile.NamedTemporaryFile(delete=False, dir='.') as f:
f.write(form['file'].file.read())
saved = f.name
body = f'Uploaded to {saved}'.encode()
else:
body = b'No file'
self.send_response(200)
self.end_headers()
self.wfile.write(body)
server = HTTPServer(('localhost', 8009), UploadHandler)
print('Upload at http://localhost:8009/')
server.serve_forever()
运行结果:
arduino
Upload at http://localhost:8009/
127.0.0.1 - - [21/Aug/2025 15:54:00] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2025 15:54:05] "POST / HTTP/1.1" 200 -
浏览器看到一个上传表单,选择文件后服务器保存到当前目录,并返回文件名。
结语
今天你已从零手敲 HTTP 服务器到能跑 RESTful API + 文件上传。
切记 :http.server 只是开发玩具,线上请交给专业选手。
最后感谢阅读!欢迎关注我,微信公众号: 倔强青铜三 。
点赞、收藏、关注 ,一键三连!!欢迎 点击 【👍喜欢作者】 按钮进行 打赏💰💰,请我喝一杯咖啡☕️!