CGI(Common Gateway Interface,通用网关接口)是 Web 服务器与外部应用程序通信的标准方式。Python 可以使用 CGI 编写动态 Web 页面,处理用户提交的表单数据等。
1. CGI 概述
CGI(Common Gateway Interface,通用网关接口) 是 Web 服务器与外部程序通信的标准方式。它允许 Web 服务器执行外部程序(如 Python 脚本),并将其输出作为 HTTP 响应返回给浏览器。
1.1 CGI 的作用
- 处理 表单提交
- 生成 动态网页
- 访问数据库并返回 查询结果
- 读取服务器端数据并 返回 JSON/XML
2. CGI 服务器配置
CGI 脚本通常放在 cgi-bin 目录下,且需要 执行权限。
2.1 配置 Apache 服务器
- 启用 CGI 模块
sudo a2enmod cgi
- 修改 Apache 配置
编辑/etc/apache2/sites-enabled/000-default.conf:
<Directory "/usr/lib/cgi-bin">
Options +ExecCGI
AddHandler cgi-script .cgi .py
</Directory>
- 重启 Apache
sudo systemctl restart apache2
2.2 使用 Python 内置 HTTP 服务器
python3 -m http.server --cgi 8000
访问 http://localhost:8000/cgi-bin/test.py 即可执行 CGI 脚本。
3. 编写 Python CGI 脚本
创建 cgi-bin/hello.py:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
print("Content-Type: text/html") # HTTP 头部
print() # 空行,分隔头部和内容
print("<html><body><h2>Hello, CGI!</h2></body></html>")
赋予执行权限:
chmod +x cgi-bin/hello.py
浏览器访问:
http://localhost/cgi-bin/hello.py
4. 处理 GET 和 POST 请求
4.1 处理 GET 请求
如果访问:
http://localhost/cgi-bin/get.py?name=Tom&age=25
可以读取 URL 参数:
#!/usr/bin/python3
import cgi
print("Content-Type: text/html\n")
form = cgi.FieldStorage()
name = form.getvalue("name", "匿名")
age = form.getvalue("age", "未知")
print(f"<html><body><h2>姓名: {name}, 年龄: {age}</h2></body></html>")
4.2 处理 POST 请求
HTML 表单:
<form action="/cgi-bin/post.py" method="post">
名字: <input type="text" name="username">
<input type="submit" value="提交">
</form>
Python 处理:
#!/usr/bin/python3
import cgi
form = cgi.FieldStorage()
username = form.getvalue("username", "游客")
print("Content-Type: text/html\n")
print(f"<h2>你好, {username}!</h2>")
5. 读取 CGI 环境变量
服务器通过环境变量传递信息:
#!/usr/bin/python3
import os
print("Content-Type: text/html\n")
print("<html><body><h2>环境变量</h2><ul>")
for key, value in os.environ.items():
print(f"<li><b>{key}</b>: {value}</li>")
print("</ul></body></html>")
6. 处理文件上传
HTML 上传表单:
<form action="/cgi-bin/upload.py" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="上传">
</form>
Python 处理:
#!/usr/bin/python3
import cgi, os
form = cgi.FieldStorage()
file_item = form["file"]
if file_item.filename:
filename = os.path.basename(file_item.filename)
with open(f"/tmp/{filename}", "wb") as f:
f.write(file_item.file.read())
print("Content-Type: text/html\n")
print(f"<h2>文件 {filename} 上传成功!</h2>")
7. 使用 Cookie
7.1 设置 Cookie
import http.cookies
cookie = http.cookies.SimpleCookie()
cookie["username"] = "Tom"
cookie["username"]["max-age"] = 3600
print("Content-Type: text/html")
print(cookie)
print()
print("<h2>Cookie 已设置!</h2>")
7.2 读取 Cookie
import os, http.cookies
cookie_string = os.environ.get("HTTP_COOKIE", "")
cookies = http.cookies.SimpleCookie(cookie_string)
username = cookies.get("username")
print("Content-Type: text/html\n")
print(f"<h2>欢迎回来, {username.value if username else '游客'}!</h2>")
8. Session 管理
CGI 不能直接管理 Session,需要手动实现:
import uuid, os
session_id = str(uuid.uuid4())
session_dir = "/tmp/sessions"
os.makedirs(session_dir, exist_ok=True)
with open(f"{session_dir}/{session_id}", "w") as f:
f.write("user=Tom")
print("Content-Type: text/html")
print(f"Set-Cookie: session_id={session_id}")
print()
print("<h2>Session 创建成功!</h2>")
9. 安全性
- 避免 XSS:使用
html.escape()处理用户输入:
import html
safe_input = html.escape("<script>alert('XSS')</script>")
- 避免 SQL 注入:使用参数化查询:
cursor.execute("SELECT * FROM users WHERE name = %s", (username,))
- 防止文件上传攻击:
allowed_types = ["image/png", "image/jpeg"]
if file_item.type not in allowed_types:
print("不允许的文件类型")
10. CGI vs. WSGI
| 特性 | CGI | WSGI |
|---|---|---|
| 进程管理 | 每个请求新进程 | 运行一个长期进程 |
| 性能 | 低 | 高 |
| 适用场景 | 小型项目 | 现代 Web 应用 |
WSGI 推荐使用 Flask 或 Django 代替 CGI。
11. 部署 CGI
- 服务器安装
Apache并启用cgi - 确保
cgi-bin目录可执行:
chmod +x /usr/lib/cgi-bin/
- 上传
*.pyCGI 脚本,访问 URL。
12. CGI 实战项目:构建一个留言板
为了更好地理解 CGI 编程,我们使用 Python + CGI + SQLite 实现一个简单的 Web 留言板,用户可以提交留言并在页面上显示所有留言。
12.1 项目结构
/var/www/cgi-bin/
│── index.html # 前端 HTML 页面
│── post_message.py # 处理留言的 CGI 脚本
│── get_messages.py # 获取所有留言的 CGI 脚本
│── messages.db # SQLite 数据库文件
12.2 创建 SQLite 数据库
首先,我们创建一个 SQLite 数据库 messages.db 并添加 messages 表:
#!/usr/bin/python3
import sqlite3
conn = sqlite3.connect("messages.db")
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
message TEXT NOT NULL
)
""")
conn.commit()
conn.close()
print("数据库初始化完成")
执行该脚本,创建数据库。
12.3 创建留言 HTML 页面
新建 index.html:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>留言板</title>
</head>
<body>
<h2>留言板</h2>
<form action="/cgi-bin/post_message.py" method="post">
<label>姓名: <input type="text" name="name" required></label><br>
<label>留言: <textarea name="message" required></textarea></label><br>
<input type="submit" value="提交">
</form>
<h3>所有留言</h3>
<div id="messages"></div>
<script>
async function fetchMessages() {
const response = await fetch('/cgi-bin/get_messages.py');
document.getElementById('messages').innerHTML = await response.text();
}
fetchMessages();
</script>
</body>
</html>
12.4 处理用户留言
创建 post_message.py 处理留言提交:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import cgi, sqlite3
print("Content-Type: text/html\n")
# 获取表单数据
form = cgi.FieldStorage()
name = form.getvalue("name", "匿名")
message = form.getvalue("message", "")
if not message:
print("<h3>留言内容不能为空!</h3>")
exit()
# 连接数据库
conn = sqlite3.connect("messages.db")
cursor = conn.cursor()
# 插入数据
cursor.execute("INSERT INTO messages (name, message) VALUES (?, ?)", (name, message))
conn.commit()
conn.close()
# 页面跳转
print("<h3>留言提交成功!<a href='/index.html'>返回</a></h3>")
12.5 获取所有留言
创建 get_messages.py:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sqlite3
print("Content-Type: text/html\n")
# 连接数据库
conn = sqlite3.connect("messages.db")
cursor = conn.cursor()
# 读取所有留言
cursor.execute("SELECT name, message FROM messages ORDER BY id DESC")
messages = cursor.fetchall()
conn.close()
# 渲染 HTML
if messages:
for name, message in messages:
print(f"<p><b>{name}:</b> {message}</p>")
else:
print("<p>暂无留言</p>")
13. CGI 进阶优化
14.1 优化 CGI 处理性能
CGI 在每次请求时都会创建新进程,影响性能,可使用:
- FastCGI(nginx + uwsgi)
- WSGI(Flask、Django)
- 持久化连接(如
mod_wsgi)
14.2 处理并发请求
CGI 适用于小型应用,但不适合高并发。可使用:
- Nginx + uWSGI
- Python Flask/Django
示例 Flask 版本:
from flask import Flask, request, render_template
import sqlite3
app = Flask(__name__)
@app.route('/')
def index():
return render_template("index.html")
@app.route('/post', methods=['POST'])
def post_message():
name = request.form.get("name", "匿名")
message = request.form.get("message", "")
conn = sqlite3.connect("messages.db")
cursor = conn.cursor()
cursor.execute("INSERT INTO messages (name, message) VALUES (?, ?)", (name, message))
conn.commit()
conn.close()
return "留言提交成功"
if __name__ == '__main__':
app.run(debug=True)
这样就用 Flask 替代了 CGI,提高了性能。
15. CGI 安全防护
15.1 防止 SQL 注入
永远使用 参数化查询:
cursor.execute("INSERT INTO messages (name, message) VALUES (?, ?)", (name, message))
避免:
cursor.execute(f"INSERT INTO messages (name, message) VALUES ('{name}', '{message}')") # SQL 注入风险
15.2 防止 XSS(跨站脚本攻击)
使用 html.escape() 处理用户输入:
import html
safe_message = html.escape(message)
通过本教程,我们学会了:
✅ CGI 服务器配置
✅ Python CGI 处理 GET/POST
✅ CGI 处理文件上传
✅ CGI 使用 Cookie 和 Session
✅ 构建留言板应用
✅ 安全性防护(SQL 注入 & XSS)
更多详细内容请关注其他相关文章!