配置API
前言
Flask 定义 API 的几种方式:app.route()、app.METHOD()、app.add_url_rule()、蓝图、类视图,以及响应基础。
请求解析、request 对象、规范 API 响应见 解析请求和处理响应。
使用 app.route()
装饰器 app.route() 是 Flask 最基本的声明 API 的方式:
from flask import Flask
app = Flask(__name__)
@app.route("/r1", methods=["GET"])
def r1():
return "r1 api"
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
使用 app.METHOD()
Flask 2.0 起支持 app.get()、app.post() 等快捷装饰器:
from flask import Flask
app = Flask(__name__)
@app.get("/r2")
def r2():
return "r2 api"
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
使用 app.add_url_rule()
app.add_url_rule() 可以动态添加路由:
app.add_url_rule(rule, endpoint=None, view_func=None, **options)
# rule: 要定义的 URL 规则或路径
# endpoint: 视图函数的名称(可用于 url_for 反向引用)
# view_func: 处理请求的视图函数
# **options: 其它路由配置,例如请求方法、URL 变量等
示例:
from flask import Flask
app = Flask(__name__)
def r3():
return "r3 api"
app.add_url_rule("/r3", view_func=r3)
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
使用蓝图
蓝图(Blueprint)是组织大型 Flask 应用的模块化工具,可以包含路由、视图函数、模板、静态文件等,并像独立应用一样使用。主要优点:
- 模块化: 将大型应用分解为更小、可管理的模块。
- 代码复用: 蓝图可以在不同应用中重复使用。
- 命名空间: 为路由和模板提供独立命名空间,避免命名冲突。
from flask import Blueprint, Flask
app = Flask(__name__)
# 创建蓝图对象,url_prefix 设置统一前缀
bp = Blueprint("api", __name__, url_prefix="/api")
@bp.get("/r4") # 实际路径: /api/r4
def r4():
return "r4 api"
app.register_blueprint(bp) # 蓝图需要注册到 Flask 实例
使用类视图
类视图是使用类处理请求的方式,与函数视图相比更适合复杂逻辑的组织与复用。
View:实现 dispatch_request
from flask import Flask, request
from flask.views import View
app = Flask(__name__)
class R5View(View):
methods = ["GET"]
def dispatch_request(self):
if request.method == "GET":
return "get r5 api"
app.add_url_rule("/r5", view_func=R5View.as_view("r5"), methods=["GET"])
MethodView:按 HTTP 方法分发
from flask.views import MethodView
class R6View(MethodView):
def get(self, user_id: int):
return f"get r6 api, user_id is {user_id}"
def post(self, user_id: int):
return f"post r6 api, user_id is {user_id}"
app.add_url_rule(
"/r6/<int:user_id>", view_func=R6View.as_view("r6"), methods=["GET", "POST"]
)
类视图 + 数据库
类视图可以结合 ORM 做更完整的业务组织(需要安装 flask-sqlalchemy):
from flask import Flask, render_template
from flask.views import View
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///users.db"
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(80), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
class ShowUsers(View):
def dispatch_request(self):
users = db.session.execute(db.select(User)).scalars().all()
return render_template("users.html", objects=users)
# as_view() 的参数是视图名称,用于 url_for() 反查
app.add_url_rule("/users/", view_func=ShowUsers.as_view("show_users"))
提示
类视图与蓝图是两种互补的模块化手段:蓝图组织"路由分组",类视图组织"单个路由的处理逻辑"。
处理响应基础
响应纯文本
from flask import Flask, make_response
app = Flask(__name__)
@app.get("/text")
def r1():
resp = make_response("hello world")
resp.headers["Content-Type"] = "text/plain"
return resp
HTML 转义
当返回 HTML 响应时(Flask 的默认响应类型),任何模板参数都应该显式转义,防止注入攻击:
from markupsafe import escape
@app.route("/<name>")
def hello(name):
return f"Hello, {escape(name)}"
响应 JSON
from flask import Flask, jsonify
app = Flask(__name__)
@app.get("/json")
def r2():
data = {"name": "zhangsan"}
return jsonify(data), 201 # 第二个参数为状态码,默认 200
响应文件
from flask import Flask, send_from_directory
app = Flask(__name__)
@app.get("/file")
def r3():
# as_attachment=True 表示让浏览器直接下载而不是打开
return send_from_directory(".", path="demo.py", as_attachment=True)
响应重定 向
from flask import Flask, redirect
app = Flask(__name__)
@app.get("/redirect")
def r4():
# 301: 永久重定向
# 302: 临时重定向
# 307: 请求方法不变,临时重定向
# 308: 请求方法不变,永久重定向
return redirect("http://www.baidu.com", 302)
自定义响应头
from flask import Flask, make_response
app = Flask(__name__)
@app.get("/headers")
def r5():
resp = make_response("get headers")
resp.headers["X-Custom-Header"] = "Custom Value"
return resp
响应 Cookie
from flask import Flask, make_response
app = Flask(__name__)
@app.get("/cookies")
def r6():
resp = make_response("get cookies")
resp.set_cookie("name", "zhangsan", max_age=3600)
return resp