02 路由与JSON响应
上一篇文章中,我们用@app.route("/chat")定义了一个最简单的路由。但真正的Agent API不可能只有一个接口——你可能需要查询对话历史、管理会话、上传文件、获取Agent状态等等。
这篇文章就来深入学习Flask的路由系统和JSON响应,让你能设计出一套规范的Agent API。
一、路由基础
路由就是URL路径和处理函数的映射关系。用户访问某个URL,Flask就调用对应的函数。
@app.route("/chat")
def chat():
return "chat接口"用户访问/chat,Flask就执行chat()函数。
1.1 多个路由
一个应用可以有多个路由,每个URL对应不同的功能:
@app.route("/")
def index():
return "首页"
@app.route("/chat")
def chat():
return "对话接口"
@app.route("/health")
def health():
return "健康检查"1.2 路由末尾的斜杠
这两个路由是不一样的:
@app.route("/projects")
def projects():
return "没有斜杠"
@app.route("/about/")
def about():
return "有斜杠"规则很简单:
| 写法 | 访问/about | 访问/about/ |
|---|---|---|
@app.route("/about") | 正常响应 | 404 |
@app.route("/about/") | 自动重定向到/about/ | 正常响应 |
建议:统一用一种风格,避免混乱。API接口一般不加斜杠。
二、动态路由
有时候URL的一部分是变化的。比如你想通过URL直接查询某个会话的历史:
@app.route("/session/<session_id>")
def get_session(session_id):
# session_id会自动从URL中提取出来
return {"session_id": session_id}访问/session/abc123,session_id的值就是"abc123"。Flask会自动把URL中的变量部分提取出来,作为参数传给视图函数。
2.1 类型转换器
默认情况下,URL变量是字符串。你可以用转换器指定类型:
@app.route("/user/<username>")
def show_user(username):
# username是字符串
return {"username": username}
@app.route("/post/<int:post_id>")
def show_post(post_id):
# post_id是整数
return {"post_id": post_id}
@app.route("/price/<float:amount>")
def show_price(amount):
# amount是浮点数
return {"amount": amount}内置的转换器:
| 转换器 | 说明 | 示例 |
|---|---|---|
string | 字符串(默认),不含斜杠 | /user/john |
int | 正整数 | /post/42 |
float | 正浮点数 | /price/9.99 |
path | 字符串,可以含斜杠 | /file/a/b/c.txt |
uuid | UUID字符串 | /task/550e8400-e29b-41d4-a716-446655440000 |
2.2 Agent场景示例
动态路由在Agent API中很常用:
@app.route("/session/<session_id>/history")
def get_history(session_id):
"""获取某个会话的对话历史"""
# 后面会从数据库或记忆中查询
return {"session_id": session_id, "messages": []}
@app.route("/agent/<agent_name>/invoke", methods=["POST"])
def invoke_agent(agent_name):
"""调用指定的Agent"""
data = request.get_json()
return {"agent": agent_name, "result": "处理完成"}三、HTTP方法
默认情况下,路由只响应GET请求。但Agent API通常需要POST请求(因为要发送用户消息)。
3.1 指定方法
@app.route("/chat", methods=["GET", "POST"])
def chat():
if request.method == "POST":
# 处理POST请求:接收用户消息
data = request.get_json()
return {"reply": f"收到: {data.get('message', '')}"}
else:
# 处理GET请求:返回使用说明
return {"usage": "POST /chat with {message: '...'}"}3.2 更简洁的写法
Flask提供了按方法名拆分的快捷装饰器,不用在一个函数里写if/else:
@app.get("/health")
def health_check():
"""GET请求:健康检查"""
return {"status": "ok"}
@app.post("/chat")
def chat():
"""POST请求:发送消息"""
data = request.get_json()
return {"reply": f"收到: {data.get('message', '')}"}
@app.delete("/session/<session_id>")
def delete_session(session_id):
"""DELETE请求:删除会话"""
return {"deleted": session_id}这种方式更清晰——每个HTTP方法对应一个函数,职责单一。
3.3 常用HTTP方法
| 方法 | 用途 | Agent API示例 |
|---|---|---|
| GET | 查询数据 | 获取对话历史、查询Agent状态 |
| POST | 创建/提交数据 | 发送消息、创建新会话 |
| PUT | 更新数据 | 更新会话配置 |
| DELETE | 删除数据 | 删除会话 |
四、获取请求数据
Agent API需要从请求中获取数据。Flask的request对象提供了多种方式。
4.1 JSON请求体
这是Agent API最常用的方式——客户端发送JSON格式的数据:
from flask import request
@app.post("/chat")
def chat():
data = request.get_json()
message = data.get("message", "")
model = data.get("model", "deepseek-v4-flash")
return {"reply": f"使用{model}回复: {message}"}客户端发送:
curl -X POST http://localhost:5000/chat \
-H "Content-Type: application/json" \
-d '{"message": "你好", "model": "deepseek-v4-flash"}'4.2 URL查询参数
问号后面的参数,适合GET请求:
@app.get("/history")
def history():
page = request.args.get("page", 1, type=int)
size = request.args.get("size", 10, type=int)
return {"page": page, "size": size}访问/history?page=2&size=20,page就是2,size就是20。
request.args.get()的第三个参数type=int会自动把字符串转成整数,转换失败返回默认值。比手动int()转换更安全。
4.3 表单数据
传统HTML表单提交的数据:
@app.post("/login")
def login():
username = request.form.get("username")
password = request.form.get("password")
return {"username": username}Agent API一般用JSON,这种方式用得比较少。
4.4 请求头
有时候需要从请求头中获取信息,比如认证Token:
@app.post("/chat")
def chat():
token = request.headers.get("Authorization", "")
if not token.startswith("Bearer "):
return {"error": "未授权"}, 401
# 提取token
api_key = token.replace("Bearer ", "")
data = request.get_json()
return {"reply": f"收到: {data.get('message', '')}"}4.5 获取方式汇总
| 数据位置 | 获取方式 | 示例 |
|---|---|---|
| JSON请求体 | request.get_json() | {"message": "你好"} |
| URL参数 | request.args.get("key") | /chat?model=deepseek |
| 表单数据 | request.form.get("key") | username=xxx |
| 请求头 | request.headers.get("X-Token") | Authorization: Bearer xxx |
| URL变量 | 函数参数 | /session/<id> |
五、返回JSON响应
Agent API的响应基本都是JSON格式。Flask让这件事变得非常简单。
5.1 直接返回字典
最简单的方式——直接return一个字典或列表,Flask自动转成JSON:
@app.get("/health")
def health():
return {"status": "ok", "version": "1.0"}Flask自动帮你做了:
- 把字典序列化成JSON字符串
- 设置
Content-Type: application/json响应头 - 返回200状态码
5.2 返回列表
也可以直接返回列表:
@app.get("/models")
def list_models():
return ["deepseek-v4-flash", "deepseek-v3", "gpt-4o"]5.3 自定义状态码
默认返回200。如果需要返回其他状态码,用元组:
@app.post("/chat")
def chat():
data = request.get_json()
if not data or "message" not in data:
return {"error": "缺少message参数"}, 400 # 400 Bad Request
return {"reply": "收到"}, 201 # 201 Created元组的格式是(响应体, 状态码)。
5.4 自定义响应头
需要加额外的响应头,用三元素元组:
@app.get("/data")
def get_data():
return (
{"data": [1, 2, 3]},
200,
{"X-Request-Id": "abc123", "Cache-Control": "no-cache"},
)5.5 jsonify函数
如果你需要更精细的控制,可以用jsonify:
from flask import jsonify
@app.get("/user")
def get_user():
return jsonify(
username="张三",
role="admin",
)jsonify会自动把关键字参数转成JSON对象,同时设置正确的Content-Type。
5.6 返回值类型汇总
| 返回值 | Flask的处理 |
|---|---|
dict或list | 自动转JSON,200状态码 |
string | 返回HTML,200状态码 |
(dict, 状态码) | 自动转JSON,自定义状态码 |
(dict, 状态码, 响应头) | 自动转JSON,自定义状态码和响应头 |
Response对象 | 直接返回,完全自定义 |
六、重定向和错误
6.1 重定向
把用户引导到另一个URL:
from flask import redirect, url_for
@app.route("/")
def index():
return redirect(url_for("health")) # 重定向到 /health
@app.route("/health")
def health():
return {"status": "ok"}url_for("health")会根据函数名health自动生成对应的URL/health。用url_for而不是硬编码URL的好处是:改了路由规则,所有引用处自动更新。
6.2 主动中断请求
有些情况下需要主动返回错误,比如参数不合法:
from flask import abort
@app.post("/chat")
def chat():
data = request.get_json()
if not data:
abort(400, description="请求体不能为空")
message = data.get("message")
if not message:
abort(400, description="缺少message字段")
return {"reply": f"收到: {message}"}abort(400)会立即停止执行,返回400错误。
6.3 自定义错误响应
默认的错误页面是HTML格式的,对API不友好。你可以自定义错误处理,让错误也返回JSON:
from flask import jsonify
@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "接口不存在"}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({"error": "服务器内部错误"}), 500
@app.errorhandler(400)
def bad_request(error):
return jsonify({"error": str(error.description)}), 400这样所有错误都会返回JSON格式,前端处理起来更方便。
七、完整的Agent API骨架
把上面学到的串起来,一个结构清晰的Agent API:
from flask import Flask, request, jsonify, abort
app = Flask(__name__)
# ---- 健康检查 ----
@app.get("/health")
def health():
return {"status": "ok"}
# ---- 对话接口 ----
@app.post("/chat")
def chat():
data = request.get_json()
if not data or "message" not in data:
abort(400, description="缺少message字段")
message = data["message"]
session_id = data.get("session_id", "default")
# 这里后面会替换成真正的Agent调用
return {
"reply": f"收到: {message}",
"session_id": session_id,
}
# ---- 会话管理 ----
@app.get("/session/<session_id>")
def get_session(session_id):
return {"session_id": session_id, "messages": []}
@app.delete("/session/<session_id>")
def delete_session(session_id):
return {"deleted": session_id}
# ---- 错误处理 ----
@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "接口不存在"}), 404
@app.errorhandler(400)
def bad_request(error):
return jsonify({"error": str(error.description)}), 400
if __name__ == "__main__":
app.run(debug=True)接口列表:
| 方法 | 路径 | 功能 |
|---|---|---|
| GET | /health | 健康检查 |
| POST | /chat | 发送消息 |
| GET | /session/<id> | 查询会话 |
| DELETE | /session/<id> | 删除会话 |
这就是一个Agent API的基本骨架了。后面加上Agent逻辑、数据库、流式输出,就能变成一个完整的生产级服务。
八、总结
路由和JSON响应是Agent API的基础:
- 路由:用
@app.route()或@app.get()/@app.post()把URL绑定到函数 - 动态路由:
<变量名>从URL提取参数,支持类型转换 - 请求数据:
request.get_json()取JSON,request.args.get()取URL参数 - JSON响应:直接return字典,Flask自动转JSON
- 错误处理:
abort()中断请求,@app.errorhandler()自定义错误格式
在下一篇文章中,我们将学习Blueprint蓝图——当你的Agent API接口越来越多时,用蓝图把它们按功能拆分到不同文件中,让项目保持整洁。