Python 小而精Web开发框架Flask精通指南


文章目录

      • Flask 简介说明
      • Flask 核心依赖
      • Flask 常用扩展
      • Flask 快速启动
        • 工作流程
        • 代码示例
        • Flask 快速启动控制台
        • Flask 快速启动效果
        • Flask 启动参数
        • Flask 路由定义
          • Flask 支持的 HTTP 请求方式:
          • 路由装饰器中的参数
          • Flask 路由参数
          • Flask 路由蓝图
            • 路由蓝图的优点
            • 路由蓝图的参数
            • Flask 请求对象
            • Flask 响应对象
            • Flask 会话保持
              • Cookie 简介
              • Cookie使用示例代码
              • Session 简介
              • Session使用示例代码
              • Flask 异常处理
                • abort函数(主动抛出)
                • errorhandler 装饰器
                • Flask 钩子函数
                  • 常用钩子函数
                  • 示例代码
                  • 示例代码说明
                  • 钩子函数工作流程
                  • Flask 模板语言
                  • Flask 模版渲染
                    • 概念
                    • 示例代码
                    • Flask Web表单
                      • Web表单创建流程
                      • WTForms支持字段
                      • WTForms常用验证函数
                      • Web表单示例代码
                      • Flask 跨域资源共享
                        • 1. 安装 Flask-CORS
                        • 2. 导入并使用 Flask-CORS
                        • 3. 自定义 CORS 配置
                        • Flask 数据库操作

                          Flask 简介说明

                          Flask 是一个用 Python 编写的轻量级 Web 应用框架。

                          它的核心非常简单,但是可以通过各种插件来扩展,使其可以用来构建复杂的 Web 应用。

                          Flask 的设计目标是保持核心简单且易于使用,同时能够被扩展以适应不同的应用需求。

                          Flask框架主要特点:

                          • 轻量级:Flask 本身只提供了最基础的 Web 功能,如URL路由、请求和响应处理等。这使得 Flask 非常轻量,易于学习和使用。
                          • 易于扩展:虽然 Flask 本身功能有限,但是它可以通过各种插件来扩展,如数据库操作(Flask-SQLAlchemy)、表单处理(Flask-WTF)、用户认证(Flask-Login)等。
                          • 模板引擎:Flask 使用 Jinja2 作为其模板引擎,可以方便地生成动态 HTML 内容。

                          Flask 核心依赖

                          Flask框架的核心依赖是Werkzeug和Jinja2。

                          Werkzeug是一个遵循WSGI协议的Python函数库,提供了许多用于构建Web应用程序的工具,包括路由、调试、表单处理、会话管理、文件上传、JSON处理、安全性等。

                          Jinja2是一个Python的模板引擎,常与Flask一起使用来渲染HTML页面,它允许在HTML文件中使用变量和逻辑控制结构来动态生成内容。

                          Flask本身相当于一个内核,其他的功能都通过扩展来实现,如邮件扩展Flask-Mail、用户认证Flask-Login、数据库Flask-SQLAlchemy等。


                          Flask 常用扩展

                          这种设计理念使得Flask保持了其核心的简单性,同时鼓励开发者根据项目的具体需求,通过集成各种第三方扩展来丰富和扩展其功能。

                          这种灵活性使得Flask能够应对各种复杂的Web开发场景,同时保持代码的清晰和可维护性。

                          Flask的插件库是其强大功能的重要来源,允许你根据自己的需求,选择适合的插件来扩展Flask的功能,从而实现网站的个性化定制。

                          例如,对于数据库操作,Flask-SQLAlchemy插件提供了对SQLAlchemy的集成,使得数据库操作变得更加简单和直观;对于邮件处理,Flask-Mail插件则提供了对SMTP服务器的支持,使得发送邮件变得轻而易举。


                          用途插件名称安装方法
                          数据库操作Flask-SQLAlchemypip install flask-sqlalchemy
                          数据库迁移Flask-Migratepip install flask-migrate
                          表单处理Flask-WTFpip install flask-wtf
                          邮件处理Flask-Mailpip install flask-mail
                          后台管理Flask-Adminpip install flask-admin
                          用户认证Flask-Loginpip install flask-login
                          Token认证Flask-JWT-Extendedpip install flask-jwt-extended
                          接口频率限制Flask-Limiterpip install flask-limiter
                          Session管理Flask-Sessionpip install flask-session
                          密码生成Flask-Bcyptpip install flask-bcypt
                          缓存FLask-Cachingpip intall flask-caching
                          页面调试Flask-DebugToobarpip install flask-debugtoolbar
                          静态文静缓存Flask-Static-Digestpip install flask-static-digest
                          本地化日期时间Flask-Momentpip install flask-moment
                          集成BootStrap框架Flask-Bootstrppip install flask-bootstrap
                          开发REST风格根据Flask-RESTfulpip install flask-restful

                          Flask 快速启动


                          工作流程

                          Flask 请求-响应工作流程


                          代码示例
                          # 导入类flask.Flask
                          from flask import Flask
                          # 实例化创建一个Flask应用,app为应用的名称,__name__是一个标识Python模块的名字的变量
                          # 若当前模块是主模块,那么此模块的名字就是__main__
                          # 若当前模块是被import的,则此模块名字为文件名
                          app = Flask(__name__)
                          # app.route('/')返回一个装饰器,为函数index()绑定对应的URL,当用户访问这个URL时就会触发这个函数
                          @app.route("/")
                          def hello():
                              # 响应:返回给浏览器的数据
                              return "Hello Flask"
                          # 再添加一个路由和视图函数
                          @app.route("/index")
                          def index():
                              return "Index Test 首页测试..."
                          if __name__ == '__main__':
                              # run() 启动的时候可以添加参数
                              # debug 是开启调试牧师,开启后修改过python代码会自动重启服务
                              # port  启动指定服务器的端口号,默认5000
                              # host  启动指定主机IP地址,默认是128.0.0.1,指定为0.0.0.0代表本机所有IP
                              app.run(debug=True)
                          

                          Flask 快速启动控制台

                          Flask 快速启动效果

                          访问网址"http://127.0.0.1:5000/"


                          Flask 启动参数

                          在 Flask 中,**app.run()**方法用于启动服务器;

                          它有几个参数,可以用来配置服务器的行为。

                          下面是 app.run()方法的参数及其含义:

                          • **host:**指定服务器监听的主机地址,默认值:'127.0.0.1'也就是只在本机上可访问
                          • **port:**指定服务器监听的端口号,默认值: 5000。
                          • **debug:**如果设置为 True,服务器将运行在调试模式下。在调试模式下,服务器会在发生错误时显示详细的错误页面,而不是简单的错误消息。这对于开发和调试非常有用,但在生产环境中应该禁用调试模式。
                          • **load_dotenv:**如果设置为 True,Flask 将在启动时加载 .env 文件中的环境变量。这通常用于存储敏感信息,如数据库密码或 API 密钥。
                          • **threaded:**如果设置为 True,服务器将使用多线程来处理请求。这可以提高并发性能,但可能会增加资源消耗。
                          • **processes:**指定服务器应该使用多少个进程来处理请求。通常,这个参数只在多线程模式下有效。增加进程数可以提高并发性能,但也会增加资源消耗。
                          • **passthrough_errors:**如果设置为 True,服务器将不会捕获异常,而是将它们直接传递给 WSGI 应用程序。这通常用于在应用程序中自定义错误处理。

                          Flask 路由定义

                          在Flask中,@app.route() 装饰器发挥着至关重要的作用;

                          它负责定义和映射应用程序的URL规则;

                          在Flask中,支持多种HTTP请求方法,如GET、POST、PUT、DELETE等;

                          并且允许为每种请求方法定义不同的处理函数,以及使用 methods参数为同一个 URL 路径定义多个请求方法。


                          Flask 支持的 HTTP 请求方式:
                          • GET: 用于请求数据。
                          • POST: 用于提交数据,通常用于表单提交。
                          • PUT: 用于更新资源。
                          • DELETE: 用于删除资源。
                          • HEAD: 与 GET 类似,但服务器只返回 HTTP 头部信息,不返回实际内容。
                          • OPTIONS: 用于获取目标资源所支持的通信选项。
                          • PATCH: 用于对资源进行部分更新。
                          from flask import Flask
                          app = Flask(__name__)
                          # 可以使用 methods 参数在路由装饰器中指定多个请求方法
                          @app.route('/example', methods=['GET', 'POST'])
                          def example():
                              if request.method == 'GET':
                                  # 处理 GET 请求
                                  return 'This is a GET request'
                              elif request.method == 'POST':
                                  # 处理 POST 请求
                                  return 'This is a POST request'
                              
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          路由装饰器中的参数

                          Flask 的路由装饰器 @app.route() 和 @blueprint.route()路由蓝图有几个常用的参数:

                          • rule: 指定路由规则的字符串,通常是路径模板,例如 '/hello'。

                          • methods: 一个包含所支持 HTTP 请求方法的列表,如 ['GET', 'POST']。

                          • endpoint: 指定路由的端点名称,用于在程序中唯一标识这个路由。如果不指定,Flask 会自动为路由生成一个端点名称。

                          • strict_slashes: 如果设置为 True,则要求 URL 必须与规则完全匹配。例如,如果规则是 '/foo',那么 /foo/ 将不会匹配这个规则。默认为 False。

                          • redirect_to: 如果提供了这个参数,当路由被访问时,将会重定向到指定的 URL。这通常用于旧 URL 的重定向。

                          • subdomain: 指定子域名,例如 '.example.com'。这样,当访问 user.example.com 时,可以匹配到这个路由。

                          from flask import Flask
                          app = Flask(__name__)
                          @app.route('/users/', methods=['GET'], strict_slashes=True, endpoint='user_profile')
                          def show_user_profile(username):
                              # 显示用户资料
                              return f'User Profile for {username}'
                          @app.route('/old-url', redirect_to='/new-url')
                          def redirect_old_url():
                              # 重定向旧 URL 到新 URL
                              pass
                          @app.route('/new-url')
                          def redirect_new_url():
                              # 返回新视图函数的处理结果
                              return f"new url return"
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          Flask 路由参数

                          类型说明
                          string(默认) 仅可接受任何不包含斜杠的文本
                          int仅可接受任何不包含斜杠的整数
                          float仅可接受任何不包含斜杠的浮点数
                          path类似string,但可以接受文件路径(包含斜杠)
                          uuid仅可接受任何不包含斜杠的uuid字符串
                          any可以同时指定多种路径进行限定
                          re通过继承BaseConverter实现自定义转换器实现正则匹配

                          from flask import Flask
                          app = Flask(__name__)
                          @app.route("/")
                          def index():
                              return "Flask 路由参数"
                          @app.route("/string//")
                          # 声明了一个string类型的路由参数,这也是Flask中路由参数的默认类型
                          # 需要注意,视图函数中的参数需要和路由参数一致
                          def get_string(username):
                              print(type(username), username)
                              return f"姓名:{username}"
                          @app.route("/int//")
                          # 声明了一个int类型的路由参数,仅接受整数类型,传递其他类型时会抛出异常,哪怕传递是float类型
                          def get_int(age):
                              print(type(age), age)
                              return f"年龄:{age}"
                          @app.route("/float//")
                          def get_float(money):
                              print(type(money), money)
                              return f"存款:{money}"
                          @app.route("/path//")
                          def get_path(address):
                              print(type(address), address)
                              return f"地址:{address}"
                          @app.route("/any//")
                          def get_any(city):
                              print(type(city), city)
                              return f"城市:{city}"
                          # 导入转换器基类
                          from werkzeug.routing import BaseConverter
                          # 自定义转换器
                          class RegexConverter(BaseConverter):
                              def __init__(self, url_map, *args):
                                  super(RegexConverter, self).__init__(url_map)
                                  # 将接受的第1个参数当作匹配规则进行保存
                                  self.regex = args[0]
                          # 添加转换器到默认的转换器字典中,并指定转换器使用时名字为:re
                          app.url_map.converters['re'] = RegexConverter
                          # 使用转换器去实现自定义匹配规则(当前此处定义的规则为:3位数字)
                          @app.route('/user/')
                          def user_info(user_id):
                              return f"用户ID:{user_id}"
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          其实,Flask中内的的路由参数类型也是不同的转换器

                          # 系统自带的转换器具体使用方式在每种转换器的注释代码中有写,请留意每种转换器初始化的参数。
                          DEFAULT_CONVERTERS = { 'default':          UnicodeConverter,
                              'string':           UnicodeConverter,
                              'any':              AnyConverter,
                              'path':             PathConverter,
                              'int':              IntegerConverter,
                              'float':            FloatConverter,
                              'uuid':             UUIDConverter,
                          }
                          

                          Flask 路由蓝图

                          Flask的路由蓝图(Blueprint)是一种组织和管理Flask应用中的路由和视图的方式。

                          它允许将相关的路由和视图分组到一个单独的模块或蓝图中,然后在主应用中注册这个蓝图。

                          这样可以使代码更加模块化和可维护。


                          路由蓝图的优点
                          1. 模块化:通过将相关的路由和视图组织到一个蓝图中,可以将应用程序拆分成多个独立的模块,每个模块都有自己的路由和视图。这有助于保持代码的清晰和可维护性。
                          2. 避免命名冲突:在Flask应用中,路由和视图的名称必须是唯一的。通过使用蓝图,可以在不同的蓝图中使用相同的路由和视图名称,而不会发生冲突。
                          3. 注册蓝图:在主应用中,可以使用app.register_blueprint()方法将蓝图注册到应用中。注册时,你可以指定蓝图的前缀、子域名等参数,以便在应用中定位到该蓝图。
                          4. 分组路由:在蓝图中,可以使用@blueprint.route()装饰器来定义路由。这样,所有在该蓝图下定义的路由都会自动带上蓝图的前缀。

                          路由蓝图的参数
                          1. name:蓝图的名称。这个名称必须是唯一的,并且在 Flask 应用中用于标识这个蓝图。它通常是一个字符串,表示蓝图的名字。例如,'main' 或 'user'。
                          2. import_name:蓝图所在的模块或包的名称。这个参数用于在蓝图内部查找资源和函数。通常,这个参数设置为 __name__,即当前模块的名称。
                          3. static_folder:一个可选参数,指定静态文件文件夹的名称(默认为 'static')。这个文件夹通常包含 CSS、JavaScript 和图像文件等静态资源。
                          4. static_url_path:一个可选参数,指定静态文件的 URL 路径(默认为 '/static')。这个路径用于生成静态文件的 URL。
                          5. template_folder:一个可选参数,指定模板文件夹的名称(默认为 'templates')。这个文件夹通常包含 Flask 应用的 HTML 模板。
                          6. url_prefix:一个可选参数,为蓝图中的所有路由添加前缀。例如,如果 url_prefix 设置为 '/api',那么蓝图中的路由 /hello 将被映射到 /api/hello。
                          7. subdomain:一个可选参数,指定蓝图的子域名。例如,如果 subdomain 设置为 'admin',那么蓝图中的路由将只响应子域名为 admin 的请求。

                          Flask 路由蓝图结构


                          Flask 请求对象

                          在 Flask 框架中,Request对象是一个非常重要的全局对象;

                          它封装了来自客户端的 HTTP 请求的所有信息。

                          这个对象允许你访问请求的各个方面,如查询参数、表单数据、JSON 数据、头部信息、cookies 等。


                          属性说明
                          url当前请求的完整路径
                          method当前请求的方法
                          headers当前请求的http协议头部
                          form当前请求的表单参数及其值的字典对象
                          args当前请求的查询字符串的字典对象
                          values当前请求包含所有数据的字典对象
                          json如果mimetype是application/json,这个参数将会解析json数据,否则返回None
                          cookies当前请求的cookie名称和值的字典对象
                          files当前请求与上传文件有关的数据
                          user_agent当前请求的用户代理信息
                          remote_addr当前请求的来源

                          from flask import Flask
                          app = Flask(__name__)
                          @app.route("/request", methods=["GET", "POST"])
                          def get_request():
                              print("请求体:", request)
                              print("请求方法:", request.method)
                              
                              # GET请求的参数
                              print("请求参数:", type(request.args), request.args)    # 类字典
                              # print("取参1个:", request.args.get("name"))
                              # print("取参N个:", request.args.getlist("name"))
                              # POST请求的参数
                              # print("请求参数:", request.form)
                              # print("取参1个:", request.form.get("name"))
                              # COOKIE
                              # print("获取Cookies:", request.cookies)
                              print("完整URL:", request.url)
                              print("基础URL:", request.base_url)
                              print("主机URL:", request.host_url)
                              print("请求路径:", request.path)
                              print("请求的客户端地址:", request.remote_addr)
                              print("上传的文件:", request.files)
                              print("请求头部:", request.headers)
                              print("用户代理:", request.user_agent)  # 包括浏览器和操作系统信息
                              return "request ok!"
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          Flask 响应对象

                          在 Flask 框架中,响应对象(Response Object)代表了一个 HTTP 响应;

                          这是 Flask 应用发送回客户端(通常是浏览器)的数据。

                          Flask 使用 Werkzeug 库来处理 HTTP 请求和响应,因此 Flask 的响应对象基于 Werkzeug 的 Response 类。

                          响应对象通常包含以下内容:

                          1. 状态码:HTTP 响应的状态码,如 200(OK)、404(Not Found)等。
                          2. 响应头:HTTP 响应头,如 Content-Type、Content-Length、Set-Cookie 等。
                          3. 响应体:响应的正文内容,这可以是字符串、字节、文件对象、生成器或迭代器。
                          4. MIME 类型:指定响应体的 MIME 类型,例如 text/html 或 application/json。

                          Flask 应用中的视图函数通常会返回一个字符串或一个元组,但 Flask 会自动将这个返回值转换成一个响应对象。

                          如果视图函数返回一个字符串,Flask 会创建一个包含该字符串的响应对象,并设置正确的 MIME 类型。


                          除了字符串,视图函数还可以返回以下几种类型的响应对象:

                          • Response 对象:可以直接返回一个 Werkzeug 的 Response 对象。
                          • 元组:返回一个包含响应体和状态码的元组 (response_body, status_code)。
                          • 元组(包含响应体、状态码和响应头):返回一个包含响应体、状态码和响应头的元组 (response_body, status_code, headers)。
                          • 生成器:返回一个生成器,Flask 会将生成器的内容作为响应体发送。
                          • 文件对象:返回一个文件对象,Flask 会将该文件的内容作为响应体发送。
                          • 重定向:使用 redirect() 函数可以返回一个重定向响应,将客户端重定向到另一个 URL。
                          • JSON 响应:使用 jsonify() 函数可以返回一个包含 JSON 数据的响应。

                          from flask import Flask, jsonify, redirect, Response, send_file
                          app = Flask(__name__)
                          @app.route('/')
                          def index():
                              # 返回字符串
                              return 'Hello, World!'
                          @app.route('/json')
                          def json_response():
                              # 返回 JSON 响应
                              return jsonify({'message': 'Hello, JSON!'})
                          @app.route('/redirect')
                          def redirect_response():
                              # 返回重定向响应
                              return redirect('/')
                          @app.route('/file')
                          def file_response():
                              # 返回文件响应
                              return send_file('example.txt', as_attachment=True)
                          @app.route('/custom')
                          def custom_response():
                              # 返回自定义的 Response 对象
                              response = Response('Custom Response', status=200, mimetype='text/plain')
                              return response
                          if __name__ == '__main__':
                              app.run()
                          

                          在Flask中的Response对象是用来处理和发送HTTP响应的关键组件,它允许设置响应的内容、状态码、头部信息等,也可以使用make_response函数来创建一个Response对象


                          Flask 会话保持

                          HTTP协议的两大特性:

                          1.无连接

                          • HTTP协议每次连接只处理一个HTTP请求,服务器处理完客户端的请求并且得到客户端的应答后,就会断开连接;

                            2.无状态

                            • 每次浏览器向服务器发送请求时,服务器不会记住之前的交互,每次请求都是独立且全新的,与前后请求无直接上下文联系。

                              状态保持的需求

                              • 然而,在现实生活中,许多应用都需要知道用户的浏览状态。
                              • 例如,一个在线购物网站可能需要知道用户是否已经登录,或者他们之前浏览过哪些商品。
                              • 这些信息对于提供个性化的体验、维护用户的会话或确保数据的安全性都至关重要。

                                状态保持的方法

                                为了满足这种需求,开发者采用了两种主要的方法来实现状态保持:

                                1. 使用Cookie在客户端存储信息:Cookie是一种小型的文本文件,可以存储在用户的浏览器上。当浏览器再次请求同一个网站时,它会发送之前存储的Cookie,使服务器能够识别用户并恢复其状态。
                                2. 使用Session在服务端存储信息:与Cookie不同,Session状态是存储在服务器端的。当用户首次访问时,服务器会为其创建一个唯一的Session ID,并将其发送到用户的浏览器,通常是作为Cookie。随后,浏览器在每次请求时都会带上这个ID,使服务器能够识别并恢复用户的状态。

                          Cookie 简介

                          Cookie概念

                          • Cookie是由服务器端创建并管理的,随后被发送给客户端(通常是用户的浏览器)。
                          • 浏览器一旦接收到这些Cookie,会在本地存储一段时间,通常是基于Cookie中设置的过期时间。
                          • 在此期间,每当浏览器向同一服务器发起请求时,它都会自动附带这些Cookie。
                          • 这些Cookie的目的是帮助服务器识别并跟踪用户的会话,从而提供个性化的服务或记住用户之前的操作,如登录状态、购物车内容等。

                            Cookie使用

                            • 在Flask中,可以通过直接操作响应对象(Response object)来设置Cookie;
                            • response.set_cookie()方法允许你为特定的响应对象设置Cookie;
                            • 当你调用response.set_cookie()时,你实际上是在告诉浏览器:“请存储这个信息,并在未来的请求中发送给我。这样,服务器就能够识别出用户。

                              这个方法接收多个参数,允许定义Cookie的名称、值、过期时间等属性,如下所示。


                          属性描述
                          keyCookie的键名
                          valueCookie值
                          max_agecookie保存时间(秒), 关闭浏览器也会过期
                          expires具体过期时间, 一个datetime对象
                          domain设置Cookie可用域
                          secure如果设置为True,仅HTTPS可用
                          httponly禁止客户端的javascript读取Cookie

                          Cookie使用示例代码

                          这段代码是一个简单的登录/注销系统的实现,它使用 cookie 来跟踪用户的登录状态。

                          当用户登录时,他们的用户名会被存储在一个 cookie 中,当用户访问首页时,这个用户名会被从 cookie 中取出并显示。

                          当用户注销时,cookie 会被删除,这样用户的登录状态就会失效。


                          main.py

                          import datetime
                          from flask import Flask, render_template, request, redirect
                          app = Flask(__name__)
                          # 首页
                          @app.route("/home")
                          def home():
                              # 4.获取cookie
                              username = request.cookies.get("user")
                              return render_template("home.html", username=username)
                          # 登录
                          @app.route("/login", methods=["GET", "POST"])
                          def login():
                              if request.method == "GET":
                                  return render_template("login.html")
                              elif request.method == "POST":
                                  pass
                                  # 1.获取前段提交过来的数据
                                  username = request.form.get("username")
                                  password = request.form.get("password")
                                  # 2.模拟登录:账号密码验证
                                  if username == "admin" and password == "123456":
                                      # 登录成功
                                      response = redirect("/home")
                                      # 3.设置cookie(确保set_cookie方法调用是在返回响应对象response之前进行)
                                      # 默认关闭浏览器,cookie失效
                                      # response.set_cookie("user", username)
                                      # 设置过期时间:
                                      # max_age(秒) = 3600秒 = 1小时 * 24 * 7 = 7天
                                      # response.set_cookie("user", username, max_age=3600 * 24 * 7)
                                      # expires 指定日期
                                      # 确保expires参数设置的日期是在未来
                                      # 确保max_age和expires 参数没有同时被设置,因为同时设置可能会导致冲突
                                      response.set_cookie("user", username, expires=datetime.datetime(2024, 12, 12))
                                      return response
                                  else:
                                      return "账号或密码错误,请重试!"
                          # 注销
                          @app.route("/logout")
                          def logout():
                              response = redirect("/home")
                              # 5.删除cookie
                              response.delete_cookie("user")
                              return response
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          home.py

                            首页 

                          首页


                          {% if username %}

                          当前登录账号:{{ username }}

                          退出

                          {% else %} 登录 {% endif %}

                          login.html

                            登录 

                          登录


                          账号:

                          密码:


                          Session 简介

                          Session 概念

                          • 当用户首次访问应用时,服务器会为用户创建一个Session,并为其分配一个唯一的Session ID。
                          • 这个Session ID通常会被存储在一个Cookie中,这样浏览器在后续的请求中就可以自动携带这个ID,以便服务器能够识别并恢复用户的会话状态。
                          • 由于Session数据存储在服务器端,因此相对于存储在客户端的Cookie来说;Session数据更加安全,不容易被用户篡改;同时,Session也支持存储更复杂的数据结构,比如对象、列表等。

                            Session 使用

                            • 在Flask中,使用Session非常简单;Flask内置了一个session对象,你可以像操作字典一样来操作它。
                            • 当你往session对象中添加数据时,Flask会自动将这些数据序列化并存储到服务器端的Session中。
                            • 当用户下次发送请求时,Flask会自动从服务器端的Session中加载数据到session对象中。

                          Session使用示例代码

                          home.py和login.py文件中的代码不变,main.py文件代码修改如下

                          import datetime
                          from flask import Flask, render_template, request, redirect, session
                          app = Flask(__name__)
                          # 首页
                          @app.route("/home")
                          def home():
                              # 4.获取Session
                              username = session.get("user")
                              return render_template("home.html", username=username)
                          # 登录
                          @app.route("/login", methods=["GET", "POST"])
                          def login():
                              if request.method == "GET":
                                  return render_template("login.html")
                              elif request.method == "POST":
                                  pass
                                  # 1.获取前段提交过来的数据
                                  username = request.form.get("username")
                                  password = request.form.get("password")
                                  # 2.模拟登录:账号密码验证
                                  if username == "admin" and password == "123456":
                                      # 登录成功
                                      response = redirect("/home")
                                      # 3.设置Session
                                      session["user"] = username
                                      session.permanent = True
                                      return response
                                  else:
                                      return "账号或密码错误,请重试!"
                          # 注销
                          @app.route("/logout")
                          def logout():
                              response = redirect("/home")
                              # 5.删除Session
                              session.pop("user")
                              return response
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          Flask 异常处理


                          abort函数(主动抛出)
                          • 我们定义了一个路由/item/,它尝试从数据库中获取一个项目;
                          • 如果项目不存在(即items.get(item_id)返回None);
                          • 我们使用abort函数抛出一个404状态码,并附带一个描述性的消息。
                          from flask import Flask, abort
                          app = Flask(__name__)
                          @app.route('/item/')
                          def get_item(item_id):
                              # 模拟根据ID获取数据库中对应的项目数据
                              items = {1: "手机", 2: "电脑", 3: "平板", }
                              item = items.get(item_id)
                              if item is None:
                                  # 如果项目不存在,我们抛出404 Not Found异常
                                  abort(404, description='主动抛出异常错误:Item not found')
                              # 如果项目存在,我们返回它
                              return f'Item {item_id} found: {item}'
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          errorhandler 装饰器
                          • 这个装饰器不仅可以处理 HTTP 状态码,还可以处理 Python 异常;

                          • 当 Flask 应用程序中发生异常时,它将查找是否有与该异常匹配的 errorhandler;

                          • 在处理 HTTP 状态码时,errorhandler 装饰器通常用于处理那些你没有显式捕获的异常,或者你想要统一处理的情况。

                          from flask import Flask, abort
                          app = Flask(__name__)
                          # 定义全局的错误处理函数
                          @app.errorhandler(404)
                          def not_found_error(error):
                              # 这里可以记录错误信息,比如写入日志文件
                              # ...
                              # 然后我们返回一个友好的错误消息给用户
                              return '骚瑞~你要找的项目不存在...', 404
                          @app.route('/item/')
                          def get_item(item_id):
                              # 模拟根据ID获取数据库中对应的项目数据
                              items = {1: "手机", 2: "电脑", 3: "平板", }
                              item = items.get(item_id)
                              if item is None:
                                  abort(404, description='Item not found')
                              return f'Item {item_id} found: {item}'
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          捕获Python代码引发的异常

                          from flask import Flask
                          app = Flask(__name__)
                          # 定义处理除以零异常的函数
                          @app.errorhandler(ZeroDivisionError)
                          def division_by_zero(error):
                              return 'You cannot divide by zero!', 400
                          @app.route('/divide//')
                          def divide_numbers(a, b):
                              try:
                                  result = a / b
                              except ZeroDivisionError as e:
                                  # 如果发生除以零的错误,我们抛出异常,稍后被errorhandler捕获
                                  raise e
                              
                              return f'The result of {a} divided by {b} is {result}'
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          Flask 钩子函数

                          Flask的钩子函数(也称为信号或事件)是Flask框架提供的一种机制,允许开发者在请求处理的不同阶段插入自定义逻辑。

                          这些钩子函数不需要手动调用,而是由Flask框架在特定事件发生时自动执行。


                          常用钩子函数
                          1. before_first_request: 在处理第一个请求之前执行一次。这个钩子特别适用于那些只需要在应用启动时执行一次的初始化任务。
                          2. before_request: 在每次请求处理之前执行。这个钩子通常用于验证用户身份、解析请求数据等任务。如果这个函数返回了一个响应,那么请求处理将在此处停止,不再继续执行后续的视图函数。
                          3. after_request: 在每次请求处理之后(但在响应发送给客户端之前)执行。这个钩子允许你对即将发送给客户端的响应进行修改。
                          4. teardown_request: 无论请求处理过程中是否发生异常,都在每次请求结束后执行。这个钩子通常用于清理资源、记录日志等任务。

                          示例代码
                          from flask import Flask, abort, jsonify
                          app = Flask(__name__)
                          @app.before_first_request
                          def initialize_app():
                              print("Application is initializing...")
                              # 在这里执行一些初始化任务,例如建立数据库连接等
                          @app.before_request
                          def before_each_request():
                              print("Before processing each request...")
                              # 在这里执行一些每次请求前都需要进行的任务,例如验证用户身份
                          @app.route('/')
                          def index():
                              print("Processing the index route...")
                              return "Hello, World!"
                          @app.after_request
                          def after_request_handler(response):
                              print("After processing each request...")
                              # 在这里对响应进行修改,例如添加头部信息等
                              return response
                          @app.teardown_request
                          def teardown_request_handler(exception):
                              print("Tearing down the request...")
                              # 在这里进行资源清理、日志记录等任务
                              if exception:
                                  print("An exception occurred:", exception)
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          示例代码说明

                          在这个示例中,我们定义了四个钩子函数:initialize_app、before_each_request、after_request_handler 和 teardown_request_handler。

                          当应用启动时,initialize_app 会在处理第一个请求之前执行。

                          然后,对于每个请求,before_each_request 会在视图函数之前执行,after_request_handler 会在视图函数之后但在响应发送之前执行,而 teardown_request_handler 无论请求是否成功都会在请求结束后执行。

                          请注意,钩子函数的执行顺序是固定的,按照它们在代码中定义的顺序执行。

                          同时,这些钩子函数可以接收参数,以便在函数体内执行特定的任务。

                          例如,after_request_handler 接收一个 response参数,允许你修改响应对象;

                          而 teardown_request_handler 接收一个 exception 参数,如果有异常发生,你可以在这个函数内处理它。


                          钩子函数工作流程

                          Flask 模板语言

                          Jinja2 是一个用于 Python 的强大的模板引擎,它被广泛用于各种 Web 开发框架中,包括 Flask。

                          Jinja2 提供了一种简单的方式来动态地生成 HTML 或其他标记语言。

                          Jinja2主要特性:

                          • 变量替换:可以在模板中使用双大括号 {{ }} 来插入变量,例如 {{ name }}。当模板被渲染时,这些变量将被实际的值替换。
                          • 控制结构:Jinja2 支持多种控制结构,包括条件语句({% if %}、{% else %}、{% elif %})和循环语句({% for %})。这使得你可以在模板中进行复杂的逻辑处理。
                          • 模板继承:Jinja2 支持模板继承,这意味着你可以定义一个基础模板(包含一些通用的元素,如页头、页脚等),然后创建多个继承自这个基础模板的子模板。这可以避免重复代码,使得模板更易于管理。
                          • 过滤器:Jinja2 提供了一系列的过滤器,可以用来修改变量的值。例如,{{ name|lower }} 将把 name 变量的值转换为小写。你也可以定义自己的过滤器。
                          • 自动转义:为了防止跨站脚本攻击(XSS),Jinja2 默认会自动转义所有的变量。这意味着如果变量的值包含 HTML 代码,这些代码将被转义为对应的 HTML 实体,而不会被浏览器解析执行。
                          • 宏:Jinja2 的宏类似于 Python 中的函数,可以用来封装可重用的模板片段。

                          超链接跳转:关于Jinja2模板引擎学习文档,请看我另一篇推文


                          Flask 模版渲染


                          概念

                          在Flask中,视图函数负责处理请求并返回数据,而模板则负责将数据呈现给用户。视图函数将数据传递给模板,模板根据这些数据动态生成HTML页面。这个过程就是模板渲染。

                          Flask默认使用的模板引擎是Jinja2,它提供了丰富的语法和功能,使得开发者可以轻松地控制HTML页面的输出。例如,Jinja2支持变量替换、条件语句、循环语句等,使得开发者可以根据数据动态生成页面内容。

                          在Flask中,可以使用render_template()函数来渲染模板。该函数接受一个模板文件名作为参数,并将视图函数中的数据作为关键字参数传递给模板。模板文件通常放在templates文件夹下,使用.html作为文件扩展名。

                          当视图函数调用render_template()函数时,Flask会加载指定的模板文件,并将数据填充到模板中,生成最终的HTML页面。然后,Flask将这个页面作为响应返回给客户端。

                          模板渲染是Flask框架中非常重要的一个概念,它使得开发者可以更加灵活地控制页面的输出,提高了Web应用程序的可维护性和可扩展性。


                          示例代码

                          main.py代码

                          # 导入FLask
                          from flask import Flask, render_template, jsonify
                          #  创建Flask应用对象,参数指明存放模板的文件夹
                          app = Flask(__name__, template_folder='templates')
                          # 路由route + 视图函数
                          @app.route("/")
                          def hello_world():
                              # 响应:返回给浏览器的数据
                              return "Flask 模板渲染!"
                          # 模板渲染
                          @app.route("/index/")
                          def index():
                              # 返回字符串:支持HTML标签
                              # return "Flask Index Test..."
                              # JSON
                              # jsonify 序列化
                              # return jsonify({"name": "张三", "age": 22})
                              # 模板渲染
                              return render_template("index.html", name="法外狂徒张三")
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          index.html代码

                            Title  {# #}
                              {# #} 

                          Index Test...


                          name: {{ name }}


                          index.css代码

                          h2 { color: cadetblue;
                          }
                          

                          Flask Web表单

                          Flask的Web表单是指使用Flask框架创建和处理Web页面中的表单元素。

                          表单是Web应用程序中用于收集用户输入的重要组件,例如登录表单、注册表单、搜索表单等。Flask提供了便捷的方式来创建和处理这些表单。

                          使用Flask-WTF需要配置参数SECRET_KEY

                          CSRF_ENABLED是为了CSRF(跨域请求伪造)保护 ,SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置或根据设置的密钥生成加密令牌。

                          CSRF

                          • CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造。
                          • CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。
                          • 造成的问题:个人隐私泄露以及财产安全。

                            防止CSRF攻击

                            1. 在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值
                            2. 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token
                            3. 在用户点击提交的时候,会带上这两个值向后台发起请求
                            4. 后端接受到请求,以会以下几件事件:
                              • 从 cookie中取出 csrf_token
                              • 从 表单数据中取出来隐藏的 csrf_token 的值
                              • 进行对比
                              • 如果比较之后两值一样,那么代表是正常的请求,如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作

                          Web表单创建流程

                          安装和配置Flask-WTF扩展:

                          • Flask-WTF是一个用于处理表单的Flask扩展,它提供了表单验证、CSRF保护等功能。
                          • 首先,需要安装这个扩展,然后在Flask应用中配置它。
                          • 通常,你需要设置一个密钥(SECRET_KEY)用于表单的CSRF保护。

                            定义表单类:

                            • 使用Flask-WTF时,每个Web表单都由一个继承自FlaskForm的类表示。
                            • 这个类定义了表单中的一组字段,每个字段都用一个对象表示。
                            • 字段对象可以附加一个或多个验证函数,用于验证用户提交的输入值是否符合要求。

                              渲染表单:

                              • 在Flask的模板中,可以使用Jinja2模板引擎来渲染表单。
                              • 通过调用表单字段,它们会被渲染成HTML表单元素。
                              • 例如,一个文本框字段会被渲染成一个****元素。

                                处理表单提交:

                                • 在Flask的视图函数中,你可以处理表单的提交。
                                • 当用户提交表单时,表单数据会被POST到服务器。
                                • 你可以使用request.form来访问这些数据,并进行相应的处理,如验证、存储等。

                          WTForms支持字段
                          字段对象说明
                          StringField文本字段
                          TextAreaField多行文本字段
                          PasswordField密码文本字段
                          HiddenField隐藏文件字段
                          DateField文本字段,值为 datetime.date 文本格式
                          DateTimeField文本字段,值为 datetime.datetime 文本格式
                          IntegerField文本字段,值为整数
                          DecimalField文本字段,值为decimal.Decimal
                          FloatField文本字段,值为浮点数
                          BooleanField复选框,值为True 和 False
                          RadioField一组单选框
                          SelectField下拉列表
                          SelectMutipleField下拉列表,可选择多个值
                          FileField文件上传字段
                          SubmitField表单提交按钮
                          FormField把表单作为字段嵌入另一个表单
                          FieldList一组指定类型的字段

                          WTForms常用验证函数
                          验证函数说明
                          DataRequired确保字段中有数据
                          EqualTo比较两个字段的值,常用于比较两次密码输入
                          Length验证输入的字符串长度
                          NumberRange验证输入的值在数字范围内
                          URL验证URL
                          AnyOf验证输入值在可选列表中
                          NoneOf验证输入值不在可选列表中

                          Web表单示例代码

                          mian.py代码

                           导入FLask
                          from flask import Flask, render_template
                          from flask_wtf import FlaskForm
                          from wtforms import StringField, PasswordField, SubmitField
                          from wtforms.validators import DataRequired, Email, Length, EqualTo
                          # 创建Flask应用对象
                          app = Flask(__name__, template_folder='templates')
                          #  关闭CSRF保护
                          app.config['WTF_CSRF_ENABLED'] = False
                          #  设置秘钥,关于安全的一般都需要设置秘钥
                          app.config['SECRET_KEY'] = "kdkakikeiqkl;hjkiauhfipouhqkjklkll;kjkj1kl231"
                          class RegistrationForm(FlaskForm):
                              username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
                              email = StringField('Email', validators=[DataRequired(), Email()])
                              password = PasswordField('Password', validators=[DataRequired(), Length(min=8)])
                              confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
                              submit = SubmitField('Sign Up')
                          @app.route('/register', methods=['GET', 'POST'])
                          def register():
                              form = RegistrationForm()
                              if form.validate_on_submit():
                                  # 如果表单验证通过,执行数据库操作等
                                  username = form.username.data
                                  email = form.email.data
                                  password = form.password.data
                                  confirm_password = form.confirm_password.data
                                  # 模拟数据库操作,如判断用户名是否存在,密码加密等
                                  if username not in ["admin", "root"]:
                                      return {"message": f"{username}, register success"}
                                  else:
                                      return {"message": f"{username}, register failed"}
                              #  form数据验证没有通过
                              else:
                                  pass
                              #  如果访问不是通过submit提交上来的,即是第一次get访问,则返回模板
                              return render_template('index.html', form=form)
                          if __name__ == '__main__':
                              app.run(debug=True)
                          

                          index.html代码

                            Register 

                          Register

                          {{ form.hidden_tag() }}

                          {{ form.username.label }}
                          {{ form.username(size=20) }}
                          {% for error in form.username.errors %} [{{ error }}] {% endfor %}

                          {{ form.email.label }}
                          {{ form.email(size=20) }}
                          {% for error in form.email.errors %} [{{ error }}] {% endfor %}

                          {{ form.password.label }}
                          {{ form.password(size=20) }}
                          {% for error in form.password.errors %} [{{ error }}] {% endfor %}

                          {{ form.confirm_password.label }}
                          {{ form.confirm_password(size=20) }}
                          {% for error in form.confirm_password.errors %} [{{ error }}] {% endfor %}

                          {{ form.submit() }}


                          Flask 跨域资源共享

                          在 Flask 中实现跨域共享资源(CORS,Cross-Origin Resource Sharing)可以通过多种方法,其中最常见的是使用 Flask-CORS 扩展。这个扩展可以轻松地添加跨域资源共享支持到你的 Flask 应用中。

                          以下是使用 Flask-CORS 扩展实现跨域资源共享的步骤:


                          1. 安装 Flask-CORS

                          需要安装 Flask-CORS 扩展,pip命令如下:

                          pip install Flask-CORS
                          

                          2. 导入并使用 Flask-CORS

                          在 Flask 应用中,导入 Flask-CORS 并将其应用到你的应用实例上。

                          from flask import Flask
                          from flask_cors import CORS
                          app = Flask(__name__)
                          CORS(app)  # 在应用实例上应用 CORS
                          # ... 其他路由和视图函数
                          

                          这将为你的整个应用启用 CORS。

                          如果你只想为特定的路由启用 CORS,可以在路由函数之前使用 @cross_origin() 装饰器。

                          from flask import Flask, jsonify
                          from flask_cors import cross_origin
                          app = Flask(__name__)
                          @app.route('/api/resource', methods=['GET'])
                          @cross_origin()  # 仅对此路由启用 CORS
                          def get_resource():
                              return jsonify({'message': 'Hello, World!'})
                          # ... 其他路由和视图函数
                          

                          3. 自定义 CORS 配置

                          Flask-CORS 还允许你自定义 CORS 配置,例如指定允许的域名、请求方法等。

                          CORS(app, resources={r"/api/*": {"origins": "*"}})
                          

                          在上面的例子中,resources 参数是一个字典,其中键是路径模式,值是一个包含 CORS 配置的字典,允许来自任何域的请求访问以 /api/ 开头的路径。


                          Flask 数据库操作

                          SQLAlchemy 是一个用于 Python 的 SQL 工具包和对象关系映射(ORM)系统。

                          它为高效和高性能的数据库访问提供了全面的企业级持久性模型;

                          SQLAlchemy主要特点:

                          • 对象关系映射(ORM):SQLAlchemy 提供了一个全功能的 ORM,它允许开发者以面向对象的方式处理数据库中的数据。你可以定义数据模型(即类),SQLAlchemy 会自动将它们映射到数据库表。
                          • 数据表达语言(DDL):SQLAlchemy 提供了一种 Pythonic 的方式来生成和执行 SQL 语句,包括创建和删除表,插入、更新和删除数据等。
                          • SQL 表达语言:SQLAlchemy 提供了一种构造 SQL 查询的 DSL(领域特定语言)。这种 DSL 提供了丰富的查询构造选项,并且可以跨多种数据库后端使用。
                          • 数据库抽象层:SQLAlchemy 提供了一种数据库抽象层,使得你可以使用相同的代码来操作不同的数据库系统(如 MySQL、PostgreSQL、SQLite 等)。
                          • 事务和会话管理:SQLAlchemy 提供了强大的事务和会话管理功能,使得你可以方便地处理数据库事务。
                          • 连接池:SQLAlchemy 内置了连接池功能,可以有效地管理数据库连接,提高应用性能。

                          超链接跳转:关于SQLAlchemy学习文档,请看我另一篇推文

                          安装插件

                          pip install flask-sqlalchemy
                          

                          初始化插件

                          from flask import Flask
                          from flask_sqlalchemy import SQLAlchemy
                          app = Flask(__name__)
                          app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:db.sqlite'
                          db = SQLAlchemy(app)
                          

                          定义模型

                          class User(db.Model):
                              id = db.Column(db.Integer, primary_key=True)
                              ...
                          

                          创建数据库表

                          with app.app_context():
                              db.create_all()