W
e
l
c
o
m
e
: )

16.Flask入门

本篇导航

本篇是 Flask 系列的第 1 篇,系统讲解 Flask 的入门与基础用法(基于 Flask 3.x):

  • §1 入门 —— Flask 介绍、安装、第一个程序、启动运行
  • §2 运行方式 —— app.run() / python 文件 / flask run 三种方式与参数
  • §3 Debug 模式与配置加载 —— 为什么要 Debug、7 种配置参数加载方式
  • §4 URL 与函数的映射(动态路由) —— 路径参数、6 种内置转换器、自定义转换器(regex / to_python / to_url)
  • §5 Postman —— API 调试工具的安装与使用
  • §6 参数获取 —— 查询参数、请求体、文件上传、request 对象
  • §7 url_for —— 反向生成 URL 及其使用原因
  • §8~§10 响应 —— 重定向、响应内容(字符串 / JSON / 元组)、自定义响应(Response / make_response)

配套篇目:17.Flask模板、18.Flask视图、19.Flask高级、20.Flask数据库。

原文勘误一览(Flask 3.x 适配)

原文基于 Flask 1.x/2.x 编写,以下写法在当前 Flask 3.x 下已不适用(正文相应位置有详细说明与实测):

位置 原文写法 现状(Flask 3.1.3 实测) 正确写法
§3.2.6.1 app.config.from_json(...),注「2.1 废弃」 已移除,调用抛 AttributeError app.config.from_file(f, json.load)
§3.2.7 DEBUG=True + from_envvar('DEBUG') 语义误解,会报 FileNotFoundError 环境变量的值填配置文件路径
§9.2 app.config["JSON_AS_ASCII"] = False 该键已移除,不生效 app.json.ensure_ascii = False

另有若干拼写与代码错误——属性名 app.deubg→app.debug、8000 接口→8000 端口、
Flask-Bable→Flask-Babel、string 转换器示例类型注释(float→str)、int 示例代码块缺缩进——均已在正文直接修正。

1 Flask 入门

Pasted-image-20250611202547

1.1 Flask 的介绍

Flask 是一款发布于 2010 年非常流行的 Python Web 框架。

特点

  • 微框架、简洁,给开发者提供了很大的扩展性。
  • Flask 和相应的插件写得很好,用起来很爽。
  • 开发效率非常高,比如使用 SQLAlchemy 的 ORM 操作数据库可以节省开发者大量书写 sql 的时间。

Flask 的灵活度非常之高,他不会帮你做太多的决策,很多都可以按照自己的意愿进行更改。

比如

使用 Flask 开发数据库的时候,具体是使用 SQLAlchemy 还是 MongoEngine,选择权完全掌握在你自己的手中。

Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展 Flask-Mail,用户认证 Flask-Login,数据库 Flask-SQLAlchemy),都需要用第三方的扩展来实现。

Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。

其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。

Pasted-image-20250611202853

扩展列表:http://flask.pocoo.org/extensions/

  • Flask-SQLAlchemy:操作数据库;
  • Flask-Script:插入脚本;
  • Flask-Migrate:管理迁移数据库;
  • Flask-Session:Session 存储方式指定;
  • Flask-WTF:表单;
  • Flask-Mail:邮件;
  • Flask-Babel:提供国际化和本地化支持,翻译;
  • Flask-Login:认证用户状态;
  • Flask-OpenID:认证;
  • Flask-RESTful:开发 REST API 的工具;
  • Flask-Bootstrap:集成前端 Twitter Bootstrap 框架;
  • Flask-Moment:本地化日期和时间;
  • Flask-Admin:简单而可扩展的管理接口的框架。

文档地址

  1. 中文文档(http://docs.jinkan.org/docs/flask/)
  2. 英文文档(http://flask.pocoo.org/docs/1.0/)

1.2 Flask 的安装

pip install flask

1.3 第一个 Flask 程序

# 从 flask 包中导入 Flask 类
# 因为需要创建一个 web 应用,而 Flask 中创建应用的对象是 Flask,因此引入此对象
from flask import Flask

app = Flask(__name__)

# @app.route:是一个装饰器
# @app.route('/')就是将 url 中 / 映射到 index 这个视图函数上面
# 以后你访问我这个网站的 / 目录的时候会执行 index 这个函数,然后将这个函数的返回值返回给浏览器
@app.route("/")
def index():
    # return 代表将数据返回给浏览器
    return "Hello World"

# 启动这个 WEB 服务
if __name__ == "__main__":
    # 默认为 5000 端口
    app.run()
    # 使用 8000 端口
    # app.run(port=8000)

1.4 启动运行

python helloworld.py

Pasted-image-20250611203748

2 运行方式

Pasted-image-20250611203925

2.1 通过对象运行

运行程序时,可以指定运行的主机 IP 地址,端口。

app.run(host="0.0.0.0", port=5000)  # 127.0.0.1

2.1.1 host 与 port 参数

  • host
    • 主机 IP 地址,可以不传
    • 默认 127.0.0.1(即 localhost)
  • port
    • 端口号,可以不传
    • 默认 5000

2.2 通过 Python 运行方式运行

在终端中使用 python 命令来运行程序,或者在 IDE 中右键运行程序。

app = Flask(__name__)

@app.route("/")
def index():
    return "hello world"

if __name__ == '__main__':
    app.run()

Pasted-image-20250828185228

提示

如果想在同一个局域网下的其他电脑访问自己电脑上的 Flask 网站,需要设置 host='0.0.0.0' 才能访问得到。

2.3 通过 Flask 自带命令运行

在终端中使用 flask 自带的 flask run 命令运行。该方式在程序中不用再写 app.run()。

app = Flask(__name__)

@app.route("/")
def index():
    return "hello world"

# 程序中不用再写 app.run()
# linux 中增加环境变量,指定要运行哪个 flask 程序
$ export FLASK_APP=helloworld
# 运行 flask 程序
$ flask run
 * Running on http://127.0.0.1:5000/

可以通过命令参数来指定运行的主机 IP 地址,端口。

flask run -h 0.0.0.0 -p 8000
注意

命令行下,可以使用使用简写。

可以通过 flask run --help 获取帮助。

-h, --host TEXT                 The interface to bind to.
-p, --port INTEGER              The port to bind to.
--reload / --no-reload          Enable or disable the reloader. By default
                                the reloader is active if debug is enabled.
--debugger / --no-debugger      Enable or disable the debugger. By default
--help                          Show this message and exit.

2.4 总结

flask 程序运行方式:

  1. 通过 app.run 运行
    • 终端:python filename
    • IDE 中:右键 - run python file in terminal
  2. 通过 flask 命令运行
    命令:flask run
    代码中可以不写 app.run()
    文件名不要用中文

3 Debug 模式与配置参数加载

Pasted-image-20250611204817

3.1 为什么要用 Debug 模式

我们先看以下两个场景:

3.1.1 场景一:修改代码后页面不更新

书写一个 flask 程序:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello World!"

if __name__ == '__main__':
    app.run()

启动程序,运行正常。

Pasted-image-20250828204603

现在我们想更改返回给浏览器的数据:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello Python!"

if __name__ == '__main__':
    app.run()

刷新页面,我们发现页面内容并没有发生变化。

Pasted-image-20250828204603

此时我们想要看到修改后的页面,需要重启 flask 程序才行。但是这样对我们开发者来说并不友好,我们后期可能会修改很多次页面,不能每修改一次就重启一次程序。有没有什么办法呢?

Pasted-image-20250828204912

3.1.2 场景二:报错信息只在控制台

在代码中制作一个错误:

@app.route('/')
def hello_world():
    a = 1
    b = 0
    c = a / b
    return 'Hello World!'

if __name__ == '__main__':
    app.run()

启动并访问:

Pasted-image-20250611204935

控制台倒是给出了错误提示信息,但是我们希望在浏览器也能有相应的提示信息。

3.2 开启 Debug 模式和配置参数的加载

我们通过 Debug 模式参数的加载来演示怎样进行参数的加载。

3.2.1 运行时传递参数

app.run(debug=True)

Pasted-image-20250828205430

3.2.2 通过 app.debug 属性设置

app.debug = True
app.run()

3.2.3 通过 config 配置参数

app.config.update(DEBUG=True)
# app.config['DEBUG'] = True
app.run()

3.2.4 通过 mapping 加载

app.config.from_mapping({'DEBUG': True})
app.run()

3.2.5 通过配置对象设置 config

class Config:
    DEBUG = True

app.config.from_object(Config)
app.run()

3.2.6 通过配置文件设置 config

3.2.6.1 json 文件

config.json

{"DEBUG": true}

app.py

import json

# ✅ Flask 2.3+ / 3.x 推荐写法
app.config.from_file('./config.json', json.load)
原文勘误:from_json 在 Flask 3.x 已移除

原文使用 app.config.from_json('config.json'),并注释「该方法在 Flask 2.1 会被废弃」。
实测(Flask 3.1.3):该方法已被彻底移除——hasattr(app.config, 'from_json') 返回 False,
调用直接抛 AttributeError: 'Config' object has no attribute 'from_json'。
准确说法是 Flask 2.3 起移除(而非「2.1 废弃」),替代写法即上方 app.config.from_file(f, json.load)。

补充:{"DEBUG":"True"} 里是字符串,不是布尔值

原文 config.json 写作 {"DEBUG":"True"},其中 "True" 是字符串而非布尔 true。
加载后 app.config["DEBUG"] 得到的是字符串 "True"(因非空字符串为真值而「碰巧」生效)。
规范写法为 {"DEBUG": true}——小写、不加引号才是布尔值。

# ❌ 旧写法(Flask 3.x 下会报 AttributeError)
# DeprecationWarning: 'from_json' is deprecated and will be removed in Flask 2.1.
# app.config.from_json('config.json')
3.2.6.2 py 文件

config.py

DEBUG = True

app.py

app.config.from_pyfile('config.py')

3.2.7 通过环境变量

app.config.from_envvar() 读取的是「环境变量的值 = 一个配置文件的路径」,而不是把环境变量的值直接当配置。

# 环境变量的值指向一个配置文件路径
export MYAPP_SETTINGS=/path/to/config.py
# 加载环境变量 MYAPP_SETTINGS 所指向的那个配置文件
app.config.from_envvar('MYAPP_SETTINGS')
原文勘误:from_envvar 的语义

原文写作「环境变量 DEBUG = True」+ app.config.from_envvar('DEBUG'),这不成立:
实测(Flask 3.1.3)from_envvar 是「加载环境变量所指向的配置文件」的快捷方式,
若环境变量的值不是文件路径,会抛 FileNotFoundError: Unable to load configuration file。
正确用法:环境变量的值填配置文件的路径,from_envvar('该环境变量名') 再去加载它。

4 URL 与函数的映射(动态路由)

Pasted-image-20250611205648

4.1 URL 路径参数

比如,有一个请求访问的接口地址为 /users/11001,其中 11001 实际上为具体的请求参数,表明请求 11001 号用户的信息。

此时如何从 url 中提取出 11001 的数据?

@app.route('/users/<user_id>')
def user_info(user_id):
    print(type(user_id))
    return f'hello user{user_id}'

其中 <user_id>,尖括号是固定写法,语法为 <variable>,variable 默认的数据类型是字符串。

4.2 Flask 框架自带的转换器类型

如果需要指定类型,则要写成 <converter:variable>,其中 converter 就是类型名称,可以有以下几种:

  1. string:如果没有指定具体的数据类型,那么默认就是使用 string 数据类型。
  2. int:数据类型只能传递 int 类型。
  3. float:数据类型只能传递 float 类型。
  4. path:数据类型和 string 有点类似,都是可以接收任意的字符串,但是 path 可以接收路径,也就是说可以包含斜杠。
  5. uuid:数据类型只能接收符合 uuid 的字符串。uuid 是一个全宇宙都唯一的字符串,一般可以用来作为表的主键。
  6. any:数据类型可以在一个 url 中指定多个路径。例如:
from flask import Flask

app = Flask(__name__)

# 默认为 string 类型
@app.route("/article/<id>")
def index(id):
    print(f'接收到的文章 ID 是:{id}')

    # 默认类型为字符串
    print(type(id))  # <class 'str'>
    # 获取到 ID 后,去数据库查询数据

    return f'返回了,{id} 的文章'

# int 类型
@app.route("/int/<int:id>")
def index_int(id):
    print(f'接收到的文章 ID 是:{id}')

    print(type(id))  # <class 'int'>
    # 获取到 ID 后,去数据库查询数据

    return f'返回了,{id} 的文章'

# float 类型
@app.route("/float/<float:id>")
def index_float(id):
    print(f'接收到的文章 ID 是:{id}')

    print(type(id))  # <class 'float'>
    # 获取到 ID 后,去数据库查询数据

    return f'返回了,{id} 的文章'

# string 类型
@app.route("/string/<string:id>")
def index_str(id):
    print(f'接收到的文章 ID 是:{id}')

    print(type(id))  # <class 'str'>
    # 获取到 ID 后,去数据库查询数据

    return f'返回了,{id} 的文章'

# path 类型:与 string 类型的区别在于可以接收 "/"
@app.route("/path/<path:id>")
def index_path(id):
    print(f'接收到的文章 ID 是:{id}')

    print(type(id))  # <class 'str'>
    # 获取到 ID 后,去数据库查询数据

    return f'返回了,{id} 的文章'

# uuid 类型
@app.route("/uuid/<uuid:id>")
def index_uuid(id):
    print(f'接收到的文章 ID 是:{id}')

    print(type(id))  # <class 'uuid.UUID'>
    # 获取到 ID 后,去数据库查询数据

    return f'返回了,{id} 的文章'

# import uuid
#
# # 生成一个 uuid 的值
# print(uuid.uuid4())  # 3cb6763f-682a-4fab-9a4a-8cdd21052b4e

# any 类型:可以接收多个 url,当路径结构相似时
# 比如用户界面和商品界面都是根据 ID 来返回具体信息
# /user/1233    返回 ID 为 1233 的用户信息
# /item/1233    返回 ID 为 1233 的商品信息
@app.route("/<any(user, item):path>/<int:id>")
def index_any(path, id):
    if path == "user":
        # 获取到 ID 后,去数据库查询数据
        return f'返回了 ID 为 {id} 的 {path} 信息'
    elif path == "item":
        # 获取到 ID 后,去数据库查询数据
        return f'返回了 ID 为 {id} 的 {path} 信息'

    return None

if __name__ == "__main__":
    app.run(debug=True)

4.2.1 int 转换器:min / max 参数

以整型匹配数据,可以如下使用:

@app.route('/users/<int:user_id>')
def user_info(user_id):
    print(type(user_id))
    return f'正在获取 ID {user_id} 的用户信息'

# 设置最小值
@app.route('/users/<int(min=1):user_id>')
def user_info(user_id):
    print(type(user_id))
    return f'hello user {user_id}'
  • 测试访问 http://127.0.0.1:5000/users/123:

    Pasted-image-20251007135002

  • 测试访问 http://127.0.0.1:5000/users/123.3:

    Pasted-image-20251007135056

注意

若是数据与设置的类型不能匹配,则会返回 Not Found。

# 设置最小值和最大值,也可单独设置 <int(min=1):user_id>
@app.route('/users/<int(min=1, max=100):user_id>')
def user_info(user_id):
    print(type(user_id))
    return f'hello user {user_id}'
  • 测试访问 http://127.0.0.1:5000/users/123.-1:

    Pasted-image-20251007135611

  • 测试访问 http://127.0.0.1:5000/users/123.1:

    Pasted-image-20251007135639

  • 测试访问 http://127.0.0.1:5000/users/123.10:

    Pasted-image-20251007135707

  • 测试访问 http://127.0.0.1:5000/users/123.100:

    Pasted-image-20251007135726

  • 测试访问 http://127.0.0.1:5000/users/123.101:

    Pasted-image-20251007135743

4.2.2 float 转换器

  • 测试访问 http://127.0.0.1:5000/float/3.1415:

    Pasted-image-20251007140241
    可以发现参数为小数时能正常访问并处理的。

  • 测试访问 http://127.0.0.1:5000/float/123:

    Pasted-image-20251007140353
    当传递参数为 123 整数的时候,则报错 Not Found。

  • 测试访问 http://127.0.0.1:5000/float/hello:

    Pasted-image-20251007140440
    当传递参数为 hello 字符串的时候,则报错 Not Found。

4.2.3 string 转换器

  • 测试访问 http://127.0.0.1:5000/string/hello:

    Pasted-image-20251007140739

  • 测试访问 http://127.0.0.1:5000/string/小龙女:

    Pasted-image-20251007140822
    可以看到中文的字符串参数也是可以直接获取的。

  • 其中如果转换器不写类型,默认就是 string 类型的,示例如下:

    # 默认为 string 类型
    @app.route("/article/<id>")
    def index(id):
        print(f'接收到的文章 ID 是:{id}')
    
        # 默认类型为字符串
        print(type(id))  # <class 'str'>
        # 获取到 ID 后,去数据库查询数据
    
        return f'返回了,{id} 的文章'
    

    测试访问 http://127.0.0.1:5000/article/小龙女:
    Pasted-image-20251007141037

4.2.4 path 转换器

# path 类型:与 string 类型的区别在于可以接收 "/"
@app.route("/path/<path:id>")
def index_path(id):
    print(f'接收到的文章 ID 是:{id}')

    print(type(id))  # <class 'str'>
    # 获取到 ID 后,去数据库查询数据

    return f'返回了,{id} 的文章'
  • 测试访问 http://127.0.0.1:5000/path/hello/小龙女//word/123:

    Pasted-image-20251007141407
    可以看到 path 转换器 可以将接收到的斜杠 / 后面的所有字符串都作为参数传递。

4.2.5 uuid 转换器

# uuid 类型
@app.route("/uuid/<uuid:id>")
def index_uuid(id):
    print(f'接收到的文章 ID 是:{id}')

    print(type(id))  # <class 'uuid.UUID'>
    # 获取到 ID 后,去数据库查询数据

    return f'返回了,{id} 的文章'
注意

后面一定要跟一个 uuid 格式的参数,可以通过以下代码生成 uuid 测试数据:

import uuid

# 生成一个 uuid 的值
print(uuid.uuid4())  # 3cb6763f-682a-4fab-9a4a-8cdd21052b4e
  • 测试访问 http://127.0.0.1:5000/uuid/3cb6763f-682a-4fab-9a4a-8cdd21052b4e:

    Pasted-image-20251007142018

  • 测试访问 http://127.0.0.1:5000/uuid/134-12442-12556-11:

    Pasted-image-20251007142050
    当传递一个不符合 uuid 格式的参数时,则报错 Not Found。

4.2.6 any 转换器

any 类型:可以匹配多个 url,当路径结构相似时。

比如用户界面和商品界面都是根据 ID 来返回具体信息。

  • /user/1233 返回 ID 为 1233 的用户信息。
  • /item/1233 返回 ID 为 1233 的商品信息。
# any 类型:可以匹配多个 url,当路径结构相似时
# 比如用户界面和商品界面都是根据 ID 来返回具体信息
# /user/1233    返回 ID 为 1233 的用户信息
# /item/1233    返回 ID 为 1233 的商品信息
@app.route("/<any(user, item):path>/<int:id>")
def index_any(path, id):
    if path == "user":
        # 获取到 ID 后,去数据库查询数据
        return f'返回了 ID 为 {id} 的 {path} 信息'
    elif path == "item":
        # 获取到 ID 后,去数据库查询数据
        return f'返回了 ID 为 {id} 的 {path} 信息'

    return None
  • 测试访问 http://127.0.0.1:5000/user/1233:

    Pasted-image-20251007142618

  • 测试访问 http://127.0.0.1:5000/item/1233:

    Pasted-image-20251007142641

  • 测试访问 http://127.0.0.1:5000/good/1233:

    Pasted-image-20251007142659
    当访问一个未定义的路径时,则报错 Not Found。

4.3 自定义转换器

Pasted-image-20250611210233

上面的这几种 Flask 自带的转换器虽然可以设置很多参数了,但是还是无法匹配一些特殊需求的路径参数。

例如:在发送短信的 URL 中,路径参数设置为手机号码,上面的自带类型就无法精确匹配手机号码了。

这时候就需要自定义路由转换器出场了。

问题

为什么路径参数可以直接识别数据类型?

提示

从 werkzeug.routing 导入 BaseConverter 类了解底层。

int 路径参数底层调用 IntegerConverter 类来作格式判断。

float 路径参数底层调用 FloatConverter 类来作格式判断。

string 路径参数底层调用 StringConverter 类来作格式判断。

4.3.1 自定义转换器的三个步骤

4.3.1.1 方式一:用 regex 属性

自定义转换器主要做 3 步:

  1. 创建转换器类,保存匹配时的正则表达式。
# 1. 创建转换器类,保存匹配时的正则表达式。
class PhoneConverter(BaseConverter):
    """
    手机号格式
    """
    regex = r"1[3-9]\d{9}"
注意

regex 名字是固定的。必须是这个。

  1. 将自定义的转换器告知 Flask 应用。
app = Flask(__name__)

# 2. 将自定义的转换器告知 Flask 应用。
# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为:mobile
app.url_map.converters['phone'] = PhoneConverter
  1. 在使用转换器的地方定义使用。
# 3. 在使用转换器的地方定义使用。
@app.route('/send/<phone:param>')
def phone(param):
    print(param)
    return f"您传递的手机号是:{param}"
  • 测试访问符合正则匹配的电话号码的 url。

    访问 http://127.0.0.1:5000/send/18912345678:

    Pasted-image-20251007145658

  • 测试访问不符合正则匹配的 url。

    访问 http://127.0.0.1:5000/send/189123:

    Pasted-image-20251007145733

4.3.1.2 方式二:带参数的转换器
  1. 自定义路由转换器的基础类:

    from werkzeug.routing import BaseConverter
    
  2. 创建自定义转换器,以及将转换器加入 Flask 应用中:

    # 创建自定义转换器
    # 1. 定义自己的转换器
    class RegexConverter(BaseConverter):
        """
        自定义转换器
        """
    
        def __init__(self, url_map, regex):
            """
    
            :param url_map: 固定传递的参数,用于调用父类的初始化方法
            :param regex: 接收从 url 使用 () 传递过来的正则参数
            """
            # 调用父类的初始化方法
            super(RegexConverter, self).__init__(url_map)
            # 将正则表达式的参数保存到对象的属性中,flask 会去使用这个属性来进行路由的正则匹配
            self.regex = regex
    
    # 2. 将自定义的转换器添加到 flask 的应用中
    app.url_map.converters["re"] = RegexConverter
    
  3. 在视图函数中设置自定义路由转换器 re:

    # 127.0.0.1:5000/send/18912345678
    # <re(r''): param> 使用 re 自定义转换器,其中 () 则是需要传递的参数
    @app.route("/send/<re(r'1[34578]\d{9}'):mobile_num>")
    def send_sms(mobile_num):
        return f"send sms to {mobile_num}"
    
  4. 测试访问符合正则匹配的电话号码的 url。

    访问 http://127.0.0.1:5000/send/18912345678:

    Pasted-image-20251007144256

  5. 测试访问不符合正则匹配的 url。

    访问 http://127.0.0.1:5000/send/189123:

    Pasted-image-20251007144504
    不符合正则匹配的 URL 参数,则报错 Not Found。从效果来看,就可以知道通过这个自定义的转换器,我们可以实现这种特殊的参数获取。

4.3.2 to_python

现在有一个需求:路径参数传递多信息并以一个参数接收。

比如:从 /user/zs+18 中获取姓名:zs、年龄:18 的信息。

实现思路:

from flask import Flask
from werkzeug.routing import BaseConverter

app = Flask(__name__)

# 需求:路径参数传递多信息并以一个参数接收。
# 比如:获取姓名:zs 年龄 18的信息
# /user/zs+18

@app.route('/')
def index():
    return "Hello World!"

@app.route("/user/<info>")
def user(info):
    args = info.split('+')
    # sql:select * from t_user where uname = args[0] and age = args[1];
    return f"获取了某某信息!!{args}"

if __name__ == '__main__':
    app.run(debug=True)

上面的方式可以完美解决我们的需求,但是我们可能在多个视图中用到上述需求,比如通过 产品名+价格 来查询数据库,这样的话我们就需要在每个有这样需求的视图中写一遍上述逻辑,有没有更好的方法呢?

在转换器类中,实现 to_python(self, value) 方法,这个方法的返回值,将会传递到 view 函数中作为参数。

from flask import Flask
from werkzeug.routing import BaseConverter

app = Flask(__name__)

# 需求:路径参数传递多信息并以一个参数接收。
# 比如:获取姓名:zs 年龄 18的信息
# /user/zs+18

class ListConverter(BaseConverter):
    def to_python(self, value):
        return value.split("+")

app.url_map.converters['list'] = ListConverter

@app.route('/')
def index():
    return "Hello World!"

@app.route("/user_info/<list:info>")
def user_info(info):
    # sql:select * from t_user where uname = info[0] and age = info[1];
    return f"获取了某某信息!!{info}"

if __name__ == '__main__':
    app.run(debug=True)

此时测试访问 http://127.0.0.1:5000/user_info/zs+18:

Pasted-image-20251007150351

从浏览器就可以判断出 to_python 方法就是 url 传递到视图函数中间的业务处理方法,其实就相当于 Django 的中间类。

如果有权限验证类的需求,可以在 to_python 中实现。

4.3.3 to_url

在转换器类中,实现 to_url(self, values) 方法,这个方法的返回值,将会在调用 url_for 函数的时候生成符合要求的 URL 形式。

class LiConverter(BaseConverter):
    # 1.在转换器类中,实现 to_python(self, value) 方法,这个方法的返回值,将会传递到 view 函数中作为参数。
    def to_python(self, value):
        return value.split('+')  # 可以对 value 进行加工后再返回

    # 2.在转换器类中,实现 to_url(self, values) 方法,这个方法的返回值,将会在调用 url_for 函数的时候生成符合要求的 URL 形式。
    def to_url(self, value):
        # return "hello"
        # ['hots','enter']---->hots+enter
        return "+".join(value)

app.url_map.converters['li'] = LiConverter

这个示例是首先访问 hello_user 视图函数,然后使用 url_for 函数跳转至 user_info 视图函数。

在这种过程中,hello_user 视图函数跳转的时候设置的路径参数为 info=["zs", "18"]。

那么正常在浏览器转发后的 url 访问应该是:http://127.0.0.1:5000/user_info/["zs", "18"]

但是由于 to_url 返回的是 "+".join(value),所以最终跳转的 url 显示会是:http://127.0.0.1:5000/user_info/zs+18。

下面来确认看看,访问 http://127.0.0.1:5000/hello_user:

Pasted-image-20251007153021

5 PostMan 的使用

Pasted-image-20250611211105

Postman 一款非常流行的 API 调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如 Jmeter、soapUI 等。不过,对于开发过程中去调试接口,Postman 确实足够的简单方便,而且功能强大。

官方网站:https://www.getpostman.com/

5.1 安装

  1. Postman 最早是作用 Chrome 浏览器插件存在的,所以,你可以到 Chrome 商店搜索下载安装,因为部分原因,所以,大家都会找别人共享的 postman 插件文件来安装。
  2. Postman 提供了独立的安装包,不再依赖于 Chrome 浏览器了。同时支持 MAC、Windows 和 Linux,推荐你使用这种方式安装。

5.2 使用

Pasted-image-20250611211253

6 Flask 中的参数获取

6.1 查询参数的获取

Pasted-image-20250611211315

我们在向服务器发起请求时,可能需要附带一些参数,比如搜索框中的内容。那么该怎样向服务器传递参数呢?

传递格式:在 url 路径后面以 ? 开头,key=value 的形式进行传参,多个参数以 & 符连接。

http://127.0.0.1:5000/test/?uname=python&pwd=123

接收方式:

from flask import Flask, request

app = Flask(__name__)

@app.route("/")
def index():
    # 获取 url 中的请求参数
    # 方式一:
    # uname = request.args.get("uname")
    # pwd = request.args.get("pwd")

    # 方式二:
    uname = request.values.get("uname")
    pwd = request.values.get("pwd")

    return f"Hello === {uname} === {pwd}"

if __name__ == '__main__':
    app.run(debug=True)
使用总结

如果你的这个页面的想要做 SEO 优化,就是被搜索引擎搜索到,那么推荐使用path 的形式。直接将传递的参数作为 path 的一部分。

如果不在乎搜索引擎优化,那么就可以使用查询字符串的形式。

6.2 请求体参数

Pasted-image-20250611211441

当我们的请求内容比较多,或者请求的信息比较敏感,不适合展示在 url 路径上时(比如用户名和密码),那该怎么向服务器发送信息呢?

答:使用 post 请求将数据放在 post 请求体中。

使用 postman 模拟 post 请求:

Pasted-image-20250829223458

获取 post 请求参数:

from flask import Flask, request

app = Flask(__name__)

# methods=["POST"] 指定可以接收请求的方式
# 不提供 methods 默认是 GET 请求,如果提供了 methods,
# 那么默认的 GET 就不生效了,如果还想要 GET 请求,必须手动指定
@app.route("/login", methods=["POST", "GET"])
def login():
    # 获取 post 中的表单数据
    # 方式一:
    # uname = request.form.get("uname")
    # pwd = request.form.get("pwd")

    # 方式二:
    uname = request.values.get("uname")
    pwd = request.values.get("pwd")

    return f"Hello === {uname} === {pwd}"

if __name__ == '__main__':
    app.run(debug=True)

6.3 上传文件

Pasted-image-20250611211512

客户端上传图片到服务器,并保存到服务器中。

使用 postman 模拟文件上传:

Pasted-image-20250829225822

注意

使用 post 请求上传文件时,请求头的 Content-Type 参数中一定要加上 multipart/form-data;,否则会失败。postman 会自动帮我们加上。我们自己写向服务器发送文件的请求时一定要记得加。

Pasted-image-20250829225934
Pasted-image-20250829230052

获取上传的图片:

from flask import Flask, request

app = Flask(__name__)

# 图片等文件数据一般都使用 post 请求中的表单数据来发送
# 发送过来的但是二进制数据
@app.route("/upload", methods=["POST", "GET"])
def upload():
    # 接收二进制数据
    pic = request.files.get("pic")
    # 读取上传文件的名字
    f_name = pic.filename
    # 将二进制数据保存为图片
    with open(f"./images/{f_name}", "wb") as f:
        f.write(pic.read())

    return f"上传成功"

if __name__ == '__main__':
    app.run(debug=True)

6.4 其它参数

Pasted-image-20250611211548

如果想要获取其他地方传递的参数,可以通过 Flask 提供的 request 对象来读取。

不同位置的参数都存放在 request 的不同属性中:

属性 说明 类型
values 记录请求的数据,并转换为字符串 *
form 记录请求中的表单数据 MultiDict
args 记录请求中的查询参数 MultiDict
cookies 记录请求中的 Cookie 信息 Dict
headers 记录请求中的报文头 EnvironHeaders
method 记录请求使用的 HTTP 方法 GET/POST
url 记录请求的 URL 地址 string
files 记录请求上传的文件 *

使用 postman 模拟请求:

Pasted-image-20250829232847

接收数据:

from flask import Flask, request

app = Flask(__name__)

@app.route("/args", methods=["POST", "GET"])
def args():
    # ====1. 获取请求的 url 地址====
    url = request.url
    # ====2. 获取请求的方式====
    method = request.method
    # ====3. 获取请求头====
    headers = request.headers
    print(headers)
    # 获取 Content-Type
    content_type = request.headers.get("Content-Type")
    # 获取 User-Agent
    user_agent = request.headers.get("User-Agent")
    # ====4. 获取 Cookie 信息====
    uid = request.cookies.get("uuid")

    return f"Hello === {url} === {method} === {content_type} === {user_agent} === {uid}"

if __name__ == '__main__':
    app.run(debug=True)

7 url_for 函数

Pasted-image-20250611211820

一般我们通过一个 URL 就可以执行到某一个函数。

如果反过来,我们知道一个函数,怎么去获得这个 URL 呢?

url_for 函数就可以帮我们实现这个功能。

7.1 url_for 函数的使用

注意

url_for 函数可以接收 1 个及以上的参数,第一个参数作为函数名。

除第一个参数外全是仅关键字参数,从第二个参数开始默认开始匹配路径参数,如果匹配不上则会添加到 URL 的后面作为查询参数。

from flask import Flask, url_for

app = Flask(__name__)

@app.route('/index')
def index():
    return f"Hello"

@app.route('/home/<int:uid>')
def home(uid):
    return f"Hello {uid}"

@app.route("/show_url")
def show_url():
    # url_for 的第一个参数是 函数名
    # url = url_for("home")

    # 从第二个参数开始默认开始匹配路径参数了
    # url = url_for("home", uid=1002)  # /home/1002

    # 从第二个参数开始默认开始匹配路径参数了
    # 匹配不上就会以查询参数进行传递
    url = url_for("home", uid=1002, addr="beijing")  # /home/1002?addr=beijing

    return f"反向查找到的 url 地址:{url}"

if __name__ == '__main__':
    app.run(debug=True)

Pasted-image-20250829235203

7.2 使用 url_for 函数原因

Pasted-image-20250611211954

问题

为什么选择 url_for 而不选择直接在代码中拼 URL 的原因?


回答
  1. 将来如果修改了 URL,但没有修改该 URL 对应的函数名,就不用到处去替换 URL 了。

  2. url_for 函数会转义一些特殊字符和 unicode 字符串。

from flask import Flask, url_for

app = Flask(__name__)

@app.route('/index/')
def index():
    return f'Hello!!'

@app.route('/show_url/')
def show_url():
    url = url_for('index', next='/', name="小龙女", other="%$#@!%^&")
    return f'反向找到的url地址是:{url}'

if __name__ == '__main__':
    app.run(debug=True)

Pasted-image-20251007162025

技巧

在定义 url 的时候,一定要记得在最后加一个斜杠。

  1. 如果不加斜杠,那么在浏览器中访问这个 url 的时候,如果最后加了斜杠,那么就访问不到。这样用户体验不太好。

  2. 加上斜杠后,浏览器中的 url 最后加不加斜杠都能正常访问。提升了用户体验。

  3. 搜索引擎会将不加斜杠的和加斜杠的视为两个不同的 url。而其实加和不加斜杠的都是同一个 url,那么就会给搜索引擎造成一个误解。加了斜杠,就不会出现没有斜杠的情况。

8 响应——重定向

Pasted-image-20250611212612

永久性重定向

http 的状态码是 301,多用于旧网址被废弃了要转到一个新的网址确保用户的访问。

比如:你输入 www.jingdong.com 的时候,会被重定向到 www.jd.com,因为 jingdong.com 这个网址已经被废弃了,被改成 jd.com 所以这种情况下应该用永久重定向。


暂时性重定向

http 的状态码是 302,表示页面的暂时性跳转。

比如:访问一个需要权限的网址,如果当前用户没有登录,应该重定向到登录页面,这种情况下,应该用暂时性重定向。

8.1 flask 中重定向

重定向是通过 redirect(location, code=302) 这个函数来实现的,location 表示需要重定向到的 URL,应该配合之前讲的 url_for() 函数来使用,code 表示采用哪个重定向,默认是 302 也即暂时性重定向,可以修改成 301 来实现永久性重定向。

from flask import Flask, redirect, url_for, request

app = Flask(__name__)

@app.route("/login/")
def login():
    return f"登录页面"

@app.route("/info/")
def info():
    if request.args.get("uname"):
        # 已经登录,展示内容
        return f"已经登录,展示内容"
    else:
        # 没有登录跳转到登录界面
        # redirect(location, code=302) 不给 code 默认为 302,即暂时重定向
        # return redirect("/login/")

        # 永久重定向
        # return redirect("/login/", code=301)

        # 登录页面的 url 改为 user_login,参数就需要改一次,
        # 如果有很多重定向到 login 页面的代码的话,改起来太麻烦了
        # 使用 url_for 函数来反向获取到 url
        return redirect(url_for("login"), code=301)

if __name__ == '__main__':
    app.run(debug=True)

9 响应——响应内容

Pasted-image-20250611213157

9.1 返回字符串

服务端返回的字符串,在浏览器实际上被解析为 text/html,即 html 内容。

from flask import Flask

app = Flask(__name__)

@app.route("/return_str/")
def return_str():
    # 服务端返回的字符串,在浏览器实际上被解析为 `text/html`,即 html 内容。
    return "你好,世界!"

Pasted-image-20250830152222

@app.route("/return_str/")
def return_str():
    # 服务端返回的字符串,在浏览器实际上被解析为 `text/html`,即 html 内容。
    return "<h3>你好,世界!</h3>"

Pasted-image-20250830152315

9.2 返回 JSON

向页面返回字典信息,会被浏览器解析为 json 数据。

@app.route("/return_json/")
def return_json():
    # 返回 json 数据
    return {"key": "Python 框架 Flask"}

Pasted-image-20250830153015

从上面的结果中我们发现页面对我们返回信息中的中文做了转码处理,如何让页面正常显示 json 中的中文数据呢?

# Flask 2.3+ / 3.x 正确写法:让 json 中的中文直接输出、不转义
app.json.ensure_ascii = False

@app.route("/return_json/")
def return_json():
    # 返回 json 数据
    return {"key": "Python 框架 Flask"}
原文勘误:JSON_AS_ASCII 在 Flask 3.x 已移除

原文 app.config["JSON_AS_ASCII"] = False 在 Flask 2.3+ / 3.x 不再生效——
实测(Flask 3.1.3):"JSON_AS_ASCII" in app.config 为 False,写入该键也不会被读取。
正确写法是 app.json.ensure_ascii = False(app.json 是 JSON 提供者,ensure_ascii 默认为 True)。

浏览器对 json 的展示

现在 Chrome 等浏览器对 json 数据展示做了优化,似乎无法直接展示中文数据,必须点击选择 美化输出 选项才能展示中文。

使用 jsonify 返回 json 数据。早期 flask 版本不支持直接返回字典作为 json 数据,所以需要使用 jsonify 函数来进行转化,现在可以直接返回字典了,该方法已无需使用。

from flask import jsonify

@app.route("/return_json1/")
def return_json1():
    # 返回 json 数据
    return jsonify({"key": "Python 框架 Flask"})

9.3 元组方式

可以返回一个元组,元组中必须至少包含一个项目,且项目应当由 (response, status)、(response, headers) 或者 (response, status, headers) 组成。status 的值会重载状态代码,headers 是一个由额外头部值组成的列表或字典。

status 值会覆盖状态代码,headers 可以是一个列表或字典,作为额外的响应头头信息。

@app.route("/return_tuple/")
def return_tuple():
    # return 'tuple',202
    # return 'tuple',{'key':'python'}
    # return 'tuple',300,{'key':'python'}
    return 'tuple', 300, [('key', 'python_sql')]

Pasted-image-20251007165147

10 响应——自定义响应

在 flask 中向页面返回内容时,最终返回的内容都被封装在了一个 Response 对象中一起发送给浏览器。所以我们可以自己创建一个 Response 对象,返回自定义的数据。

10.1 创建 Response

from flask import Flask, Response

app = Flask(__name__)

@app.route("/")
def index():
    # 设置 返回内容
    return Response("Hello World!")

from flask import Flask, Response

app = Flask(__name__)

@app.route("/")
def index():
    # 设置 返回内容
    # return Response("Hello World!")
    # 设置 返回内容 和 状态码
    return Response("Hello World!", status=500)

@app.route("/")
def index():
    # 设置 返回内容
    # return Response("Hello World!")
    # 设置 返回内容 和 状态码
    # return Response("Hello World!", status=500)
    # 设置 返回内容、状态码 和 响应头信息
    return Response("Hello World!", status=500, headers={"key": "python"})

@app.route("/")
def index():
    # 设置 返回内容
    # return Response("Hello World!")
    # 设置 返回内容 和 状态码
    # return Response("Hello World!", status=500)
    # 设置 返回内容、状态码 和 响应头信息
    # return Response("Hello World!", status=500, headers={"key": "python"})
    # 设置 状态码
    return Response(status=404)

@app.route("/")
def index():
    # 设置 返回内容
    # return Response("Hello World!")
    # 设置 返回内容 和 状态码
    # return Response("Hello World!", status=500)
    # 设置 返回内容、状态码 和 响应头信息
    # return Response("Hello World!", status=500, headers={"key": "python"})
    # 设置 状态码
    # return Response(status=404)
    # 设置 提示信息 和 状态码
    return Response("页面没有找到", status=404)

10.2 make_response 方式

from flask import make_response

@app.route("/home/")
def home():
    # 通过 make_response 方法创建一个 Response 对象
    resp = make_response("我是创建的 Response 对象")

    return resp

@app.route("/home/")
def home():
    # 通过 make_response 方法创建一个 Response 对象
    resp = make_response("我是创建的 Response 对象")
    # 设置响应头
    resp.headers.set("key", "flask+python")

    return resp

@app.route("/home/")
def home():
    # 通过 make_response 方法创建一个 Response 对象
    resp = make_response("我是创建的 Response 对象")
    # 设置响应头
    resp.headers.set("key", "flask+python")
    # 设置状态码
    resp.status = 666

    return resp

@app.route("/home/")
def home():
    # 通过 make_response 方法创建一个 Response 对象
    resp = make_response("我是创建的 Response 对象")
    # 设置响应头
    resp.headers.set("key", "flask+python")
    # 设置状态码
    # resp.status = 666
    resp.status = "666 not found"

    return resp

11 术语表(重要英文单词)

Web 框架相关

术语 全拼 / 说明 释义
WSGI Web Server Gateway Interface Python Web 服务器与框架之间的通用接口规范,Flask 基于它运行
Werkzeug — Flask 依赖的 WSGI 工具库,提供路由、请求/响应等底层实现
Jinja2 — Flask 默认使用的模板引擎
microframework micro framework 微框架,核心精简、功能主要靠扩展实现
extension — 扩展,为 Flask 增加功能的第三方插件(如 Flask-SQLAlchemy)

路由与请求

术语 全拼 / 说明 释义
route — 路由,URL 与视图函数之间的映射关系
converter — 转换器,把 URL 中的字符串参数转换成指定类型
endpoint — 端点,路由注册到视图函数时使用的名字(默认取函数名)
view function — 视图函数,接收请求、处理并返回响应的函数
query string — 查询字符串,URL 中 ?key=value&... 的部分
form data — 表单数据,位于 POST 请求体中
Content-Type — 请求/响应体的媒体类型(如 multipart/form-data)
UUID Universally Unique Identifier 通用唯一识别码,形如 3cb6763f-682a-...

响应与配置

术语 全拼 / 说明 释义
redirect — 重定向;301 永久重定向、302 暂时重定向
Response — 响应对象,封装返回给浏览器的内容、状态码与响应头
JSON JavaScript Object Notation 一种轻量的数据交换格式
url_for — 通过视图函数名反向生成 URL 的 Flask 函数
debug mode — 调试模式,开启后自动热重载,并在浏览器显示错误堆栈
config configuration 配置,Flask 的应用级参数字典

12 系列导航

13 参考链接

分类:Python

posted @ 2026-04-11 02:04  挖掘鱼  阅读(30)  评论(0)    收藏  举报