跳到主要内容

配置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
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