4. Flask基础API搭建
好,到了真正动手写代码的时候了。前面三章我们聊了推理服务的整体架构、环境准备和项目结构,现在该让服务器真正跑起来了。
这一章我们聚焦 Flask——Python 生态里最轻量的 Web 框架。为什么选它?说白了,推理服务的核心是「接收请求 → 调用模型 → 返回结果」,Flask 恰好用最少的代码帮你搞定这件事。我在生产环境里见过用 FastAPI 的、用 Django 的,但 Flask 的灵活性和低心智负担,让它成为原型验证和中小规模部署的首选。
本章核心目标:用 Flask 搭建一个能接收 POST 请求、处理 JSON 数据、返回推理结果的 REST API 端点。
4.1 Flask 框架到底是个啥?
Flask 是一个用 Python 写的微型 Web 框架。注意「微型」这个词——它核心只做一件事:把 HTTP 请求映射到你的 Python 函数上。没有 ORM、没有表单验证、没有认证中间件,这些统统需要你自己选配。
我个人习惯把 Flask 比作「一把螺丝刀」:功能单一但极其趁手。你不需要学一堆概念就能上手,这也是我推荐新手用它搭建推理服务的原因。
| 特性 | Flask | Django | FastAPI |
|---|---|---|---|
| 启动代码量 | 5-10 行 | 50+ 行 | 10-15 行 |
| 内置功能 | 极少(路由+模板) | 极多(全栈) | 中等(路由+校验) |
| 异步支持 | 需扩展 | 需扩展 | 原生 |
| 学习曲线 | 平缓 | 陡峭 | 中等 |
| 推理服务适用性 | ★★★★★ | ★★☆☆☆ | ★★★★☆ |
你看,Flask 在推理服务这个场景下几乎满分。为什么?因为推理服务不需要复杂的后台管理、不需要用户认证、不需要模板渲染——它只需要一个「输入 → 处理 → 输出」的管道。
4.2 安装与第一个「Hello World」
先装包。我建议你在虚拟环境里操作,别污染全局 Python。
pip install flask
装完写个文件,就叫 app.py:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, 推理服务!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
跑起来:
python app.py
打开浏览器访问 http://localhost:5000,看到「Hello, 推理服务!」就对了。
小提示:host='0.0.0.0' 表示监听所有网络接口,这样你局域网里的其他机器也能访问。如果只想本机访问,改成 host='127.0.0.1'。
这段代码干了什么?@app.route('/') 是一个装饰器,它告诉 Flask:当用户访问根路径 / 时,执行下面的 hello() 函数。就这么简单。
4.3 创建第一个 REST API 端点
「Hello World」太简单了,我们来写一个真正的 API 端点。假设我们要做一个文本分类的推理接口,输入一段文本,返回它的类别。
先定义一个 /predict 端点,用 GET 方法:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/predict', methods=['GET'])
def predict_get():
# 模拟推理结果
result = {
"text": "示例文本",
"label": "positive",
"confidence": 0.95
}
return jsonify(result)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
访问 http://localhost:5000/predict,你会看到:
{
"text": "示例文本",
"label": "positive",
"confidence": 0.95
}
jsonify() 是 Flask 提供的函数,它把 Python 字典转成 JSON 格式的 HTTP 响应,并自动设置 Content-Type: application/json。我在项目中见过有人手动拼 JSON 字符串,结果漏了转义字符导致前端解析失败——用 jsonify 就永远不会出这种问题。
注意:GET 请求的参数通常放在 URL 里,不适合传输大量文本数据。真正的推理服务应该用 POST 请求。
4.4 处理 POST 请求与 JSON 数据
这才是本章的重头戏。推理服务的标准流程是:客户端把输入数据(比如一段文本、一张图片的 base64)放在 HTTP 请求的 body 里,以 JSON 格式发送过来。服务端解析 JSON,调用模型,返回结果。
看代码:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict_post():
# 1. 获取请求中的 JSON 数据
data = request.get_json()
# 2. 校验数据
if not data or 'text' not in data:
return jsonify({"error": "缺少 'text' 字段"}), 400
input_text = data['text']
# 3. 模拟推理过程
# 在实际项目中,这里会调用你的模型
label = "positive" if len(input_text) > 5 else "negative"
confidence = 0.85 if label == "positive" else 0.72
# 4. 返回结果
result = {
"input": input_text,
"label": label,
"confidence": confidence
}
return jsonify(result), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
测试一下。用 curl 命令:
curl -X POST http://localhost:5000/predict \
-H "Content-Type: application/json" \
-d '{"text": "这部电影太棒了"}'
返回:
{
"input": "这部电影太棒了",
"label": "positive",
"confidence": 0.85
}
如果忘了传 text 字段:
curl -X POST http://localhost:5000/predict \
-H "Content-Type: application/json" \
-d '{"foo": "bar"}'
返回 400 错误:
{
"error": "缺少 'text' 字段"
}
这里有几个关键点:
request.get_json():解析请求 body 中的 JSON,返回 Python 字典。如果请求没有Content-Type: application/json头,这个方法会返回None。- 状态码:成功返回 200,客户端错误返回 400,服务器错误返回 500。这是 REST API 的基本礼仪。
- 错误处理:永远不要假设客户端会传正确的数据。我在生产环境里遇到过传空字符串、传数组、甚至传二进制文件的奇葩情况——校验这一步不能省。
避坑指南:我曾经在项目里忘了设置 Content-Type 头,结果 request.get_json() 一直返回 None,排查了半天。记住:客户端发请求时必须带 Content-Type: application/json,否则 Flask 不会自动解析 JSON。
4.5 完整的推理服务骨架
把上面的代码整合一下,加上模型加载的逻辑(先占位),就是一个可用的推理服务骨架:
from flask import Flask, request, jsonify
import time
app = Flask(__name__)
# 模拟模型加载
print("正在加载模型...")
time.sleep(2) # 模拟加载耗时
print("模型加载完成")
def load_model():
"""实际项目中加载你的模型"""
# 例如:model = torch.load('model.pth')
return "model_loaded"
model = load_model()
@app.route('/health', methods=['GET'])
def health_check():
"""健康检查端点"""
return jsonify({"status": "ok", "model_loaded": model is not None})
@app.route('/predict', methods=['POST'])
def predict():
# 解析请求
data = request.get_json()
if not data or 'text' not in data:
return jsonify({"error": "缺少 'text' 字段"}), 400
input_text = data['text']
try:
# 调用模型推理
# result = model.predict(input_text)
# 这里用模拟数据
label = "positive" if len(input_text) > 5 else "negative"
confidence = 0.85
return jsonify({
"input": input_text,
"label": label,
"confidence": confidence
}), 200
except Exception as e:
return jsonify({"error": f"推理失败: {str(e)}"}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
这个骨架包含了:
- 模型预加载:服务启动时加载模型,避免每次请求都加载
- 健康检查:
/health端点用于监控服务状态 - 输入校验:确保必要字段存在
- 异常捕获:推理过程中的错误不会导致服务崩溃
4.6 本章知识体系
下面这张图帮你理清 Flask 推理服务的核心流程:
这张图展示了从客户端发起 POST 请求到服务端返回结果的完整链路。你想想看,整个流程其实就四个步骤:接收、解析、推理、返回。Flask 帮我们搞定了第一步和第四步,中间两步才是你的业务逻辑。
4.7 本章小结
到这,你已经掌握了:
- Flask 的基本概念和安装
- 如何用
@app.route创建 API 端点 - GET 和 POST 请求的区别与使用场景
- 如何解析 JSON 请求体并返回 JSON 响应
- 一个完整的推理服务骨架代码