• Flask 入门


    1. 介绍

    Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。

    “微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。

    默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用

    wsgiref

    最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。

    如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

    正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块

    from wsgiref.simple_server import make_server
    
    
    def mya(environ, start_response):
        print(environ)
        start_response('200 OK', [('Content-Type', 'text/html')])
        if environ.get('PATH_INFO') == '/index':
            with open('templates/index.html', 'rb') as f:
                data = f.read()
    
        elif environ.get('PATH_INFO') == '/home':
            with open('templates/home.html', 'rb') as f:
                data = f.read()
        else:
            data = b'

    Hello, web!

    '
    return [data] if __name__ == '__main__': myserver = make_server('', 8010, mya) print('监听8010') myserver.serve_forever() # wsgiref简单应用
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2. 初步使用

    安装:pip3 install flask

    from flask import Flask
    
    # 实例化产生一个Flask对象
    app = Flask(__name__)
    # 将 '/'和视图函数hello_workd的对应关系添加到路由中
    @app.route('/') 
    def hello_world():
        return 'Hello World!'
    
    if __name__ == '__main__':
        app.run() # 最终调用了run_simple()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    新手四件套:
    返回字符串  return 字符串  					django中 HttpResponse('字符串')
    返回模板    return render_template 			django中 render(request,模板,{})
    返回重定向  return redirect('/login') 		django中 redirect('/login')
    返回json   return jsonify(字典,列表)			django中 JsonResponse
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3. 配置文件

    3.1 配置文件写法

    flask 配置文件写法很多种,如下所示:

    1. 第一种:直接配置
    app = Flask(__name__)
    app.secret_key = 'amsnjabcascasdnknfv'
    app.debug = True
    
    • 1
    • 2
    • 3
    1. 第二种:app 所有的配置项都在 app.config 这个字典中
    app = Flask(__name__)
    app.config['DEBUG'] = True
    
    • 1
    • 2
    1. 第三种:编写一个 settings.py 文件用于存放配置
    # settings.py
    NAME = 'xwx'
    
    • 1
    • 2
    app = Flask(__name__)
    # 通过py文件获取配置
    app.config.from_pyfile('settings.py')
    print(app.config['NAME'])
    
    • 1
    • 2
    • 3
    • 4
    1. 第四种:通过类的方式(常用)

    创建 settings.py 文件

    # 通用配置类
    class Config(object):
        DEBUG = False
        TESTING = False
        DATABASE_URI = 'sqlite://:memory:'
    
    # 生成配置类
    class ProductionConfig(Config):
        DATABASE_URI = 'mysql://user@localhost/foo'
    
    # 开发配置类
    class DevelopmentConfig(Config):
        DEBUG = True
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    使用

    app = Flask(__name__)
    app.config.from_object('settings.DevelopmentConfig')
    print(app.config)
    
    • 1
    • 2
    • 3
    1. 其他方式
    # 通过环境变量配置
    app.config.from_envvar("环境变量名称")
    # app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
    # 环境变量的值为python文件名称名称,内部调用from_pyfile方法
    
    • 1
    • 2
    • 3
    • 4
    app.config.from_json("json文件名称")
    # JSON文件名称,必须是 json 格式,因为内部会执行 json.loads
    
    • 1
    • 2
    app.config.from_mapping({'DEBUG': True})
    # 字典格式
    
    • 1
    • 2

    3.2 默认配置

    flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:

    {
            'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
            'TESTING':                              False,                          是否开启测试模式
            'PROPAGATE_EXCEPTIONS':                 None,                          
            'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
            'SECRET_KEY':                           None,
            'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
            'USE_X_SENDFILE':                       False,
            'LOGGER_NAME':                          None,
            'LOGGER_HANDLER_POLICY':               'always',
            'SERVER_NAME':                          None,
            'APPLICATION_ROOT':                     None,
            'SESSION_COOKIE_NAME':                  'session',
            'SESSION_COOKIE_DOMAIN':                None,
            'SESSION_COOKIE_PATH':                  None,
            'SESSION_COOKIE_HTTPONLY':              True,
            'SESSION_COOKIE_SECURE':                False,
            'SESSION_REFRESH_EACH_REQUEST':         True,
            'MAX_CONTENT_LENGTH':                   None,
            'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
            'TRAP_BAD_REQUEST_ERRORS':              False,
            'TRAP_HTTP_EXCEPTIONS':                 False,
            'EXPLAIN_TEMPLATE_LOADING':             False,
            'PREFERRED_URL_SCHEME':                 'http',
            'JSON_AS_ASCII':                        True,
            'JSON_SORT_KEYS':                       True,
            'JSONIFY_PRETTYPRINT_REGULAR':          True,
            'JSONIFY_MIMETYPE':                     'application/json',
            'TEMPLATES_AUTO_RELOAD':                None,
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    4. 路由

    django 与 flask 路由的区别:

    • 在django中,路由是浏览器访问服务器时,先访问的项目中的url,再由项目中的url找到应用中url, 这些url是放在一个列表里,遵从从前往后匹配的规则。

    • 在flask中,路由是通过装饰器给每个视图函数 提供的,而且根据请求方式的不同可以一个url用于不同的作用。

    4.1 路由典型写法

    @app.route('/detail/', methods=['GET'], endpoint='detail')
    
    • 1
    @app.route('/index', methods=['GET'], endpoint='index')
    @loginDecorator
    def index():
        return 'xwx'
    
    • 1
    • 2
    • 3
    • 4

    4.2 路由转换器

    from werkzeug.routing import DEFAULT_CONVERTERS

    #: the default converter mapping for the map.
    DEFAULT_CONVERTERS: t.Mapping[str, t.Type[BaseConverter]] = {
        "default": UnicodeConverter,
        "string": UnicodeConverter,
        "any": AnyConverter,
        "path": PathConverter,
        "int": IntegerConverter,
        "float": FloatConverter,
        "uuid": UUIDConverter,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    转换器类型数据
    defaultUnicodeConverter字符
    stringUnicodeConverter字符
    anyAnyConverter
    pathPathConverter路径
    intIntegerConverter整数
    floatFloatConverter小数
    uuidUUIDConverteruuid

    使用

    @app.route('/home/', endpoint='home', methods=['GET'])
    def home(uid):	# 接收一个参数
        print(uid)
        return render_template('home.html')
    
    • 1
    • 2
    • 3
    • 4

    4.3 路由源码分析

    • 装饰器分析

    @ 是 python 特殊语法糖,会把下面的函数当参数传入。如下有俩个装饰器的情况下,优先执行最上层的装饰器,也就是包在最外层的装饰器。

    @app.route('/index', endpoint='index')	# 先执行
    @loginDecorator							# 后执行
    def index():
        return 'xwx'
    
    • 1
    • 2
    • 3
    • 4
    • 源码分析
    def route(self, rule: str, **options: t.Any) -> t.Callable:
        def decorator(f: t.Callable) -> t.Callable:
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator
    
    # 最终执行的是 add_url_rule 函数
    
    @setupmethod
    def add_url_rule(
        self,
        rule: str,
        endpoint: t.Optional[str] = None,
        view_func: t.Optional[t.Callable] = None,
        provide_automatic_options: t.Optional[bool] = None,
        **options: t.Any,
    ) -> None:
        raise NotImplementedError
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 参数分析
    参数说明
    ruleURL规则
    view_func视图函数名称
    endpoint名称,用于反向生成URL,即: url_for(‘名称’)
    methods允许的请求方式,如:[“GET”, “POST”]
    strict_slashes对URL最后的 / 符号是否严格要求
    redirect_to重定向到指定地址
    subdomain子域名访问
    defaults默认值, 当URL中无参数,函数需要参数时,使用defaults = {‘k’: ‘v’}为函数提供参数

    所以注册路由的另外一种方式是

    app.add_url_rule('/order/', view_func=order)
    app.add_url_rule('/index', view_func=index,)
    
    • 1
    • 2

    5 基于类的视图

    class Home(MethodView):
        methods = ['GET']
    
        # decorators = (装饰器名字,装饰器名字2,)
    
        def get(self):
            return 'get'
    
        def post(self):
            return 'post'
    
    
    app.add_url_rule('/home', view_func=Home.as_view(name='home'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 'as_view' 的name是别名,之前写fbv的时候,endpoint是别名,在cbv中name就是endpoint,但
    是必须写,即便写了endpoint
    2. 'Home.as_view' 是view函数的内存地址,请求来了执行 self.dispatch_request(),MethodView
    重写了dispatch_request,其根据请求方式执行视图类中跟请求方式同名的方法,如果视图类继承了View
    需要重写dispatch_request    
    3. cbv加装饰器,在类中写 decorators = (装饰器名字1, 装饰器名字2),第一个位置的会放在最下层
    多个装饰器执行顺序是从【上往下执行】
    4. cbv只允许某个请求方式   methods=['POST']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    6. 模板

    flask 的模板语法使用第三方 jinja2,兼容 dtl,但是它可以加括号,可以使用 [ ],且处理了xss攻击

    6.1 渲染变量、变量的循环

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>用户列表</h1>
        <table>
            {% for k,v in user_dict.items() %}
            <tr>
                <td>{{k}}</td>
                <td>{{v.name}}</td>
                <td>{{v['name']}}</td>
                <td>{{v.get('name')}}</td>
                <td><a href="/detail/{{k}}">查看详细</a></td>
            </tr>
            {% endfor %}
        </table>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    6.2 逻辑判断

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>用户列表</h1>
        <table>
            {% if name %}
              <h1>Hello {{ name }}!</h1>
            {% else %}
              <h1>Hello World!</h1>
            {% endif %}
        </table>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    6.3 Markup

    除了使用 safe 外还可以使用 Markup ,其等价于 django的 mark_safe 。防止 xss 攻击是使用了 html的特殊字符替换(例如将 < 替换成 < 、> 替换成 > ),但是如果使用了 |safe 或者 Markup ,就不会渲染到页面上

    from flask import Flask,render_template,Markup,jsonify,make_response
    app = Flask(__name__)
    
    def func1(arg):
        return Markup("://www.baidu.com">点我看美女")
        
    @app.route('/')
    def index():
        return render_template('index.html',ff = func1)
    
    if __name__ == '__main__':
        app.run()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
        {{ff()}}
    	{{ff()|safe}}
    
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    7. 请求响应

    7.1 请求对象的属性和方法

    属性说明数值/类型
    request.method请求方式str
    request.argsget 请求参数ImmutableMultiDict([])
    request.formpost 提交的数据ImmutableMultiDict([])
    request.valuesget,post提交的数据总和CombinedMultiDict([ImmutableMultiDict([]), ImmutableMultiDict([])])
    request.cookiescookies
    request.headers请求头(等同于django的META)
    request.path路径/home
    request.full_path全路径/home?name=xwx
    request.script_root
    request.url带域名带参数的请求路径http://127.0.0.1:5000/home?name=xwx
    request.base_url带域名请求路径http://127.0.0.1:5000/home
    request.url_root域名+端口http://127.0.0.1:5000/
    request.host_url域名+端口http://127.0.0.1:5000/
    request.host不带http的域名+端口127.0.0.1:5000
    request.files文件ImmutableMultiDict([(‘files’, )])
    request.files.get(‘files’)文件

    7.2 响应对象的属性和方法

    属性说明
    “字符串”返回字符串
    render_template(‘html模板路径’,**{})返回模板
    redirect重定向
    jsonify返回 JSON 数据

    8. cookie、session

    cookie:存放在客户端的键值对
    session:存放在客户端的键值对
    token:存放在客户端,通过算法来校验
    
    • 1
    • 2
    • 3

    8.1 添加 cookie、session

    • cookie
      使用 make_response 包裹响应对象
    response = make_response(render_template('index.html'))
     # response 是 flask.wrappers.Response类型
    response.delete_cookie('key')
    response.set_cookie('key', 'value')
    response.headers['X-Something'] = 'A value' # 可用于跨域
    return response
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • session
      from flask import session
      除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。 (app.session_interface对象)
    设置:session['username']'xxx'
    删除:session.pop('username', None)
    
    • 1
    • 2

    在使用 session 之前必须现在设置一下密钥 secret_key

    app.secret_key="asdas"   # 值随便
    
    • 1
    • django 与 flask 的不同
    在django中
    1. 生成一个随机的字符串 
    2. 往数据库存 
    3. 写入cookie返回浏览器
    
    在 flask 中他没有数据库,但 session 是怎样实现的?
    1. 生成一个密钥写入这个cookie
    2. 然后下次请求的时候,通过这个cookie解密,然后赋值给session
    #可以通过 app.session_interface 来查看
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    8.2 flask 的 session 源码分析

    1. app.session_interface 起手
    session_interface = SecureCookieSessionInterface()
    # 发现 session_interface 是 SecureCookieSessionInterface 的对象。
    # 该类中有 open_session、save_session 方法
    
    • 1
    • 2
    • 3
    1. open_session 方法

    open_session方法当请求来了,从cookie中取出字符串,把字符串反序列化成session对象

    def open_session(
        self, app: "Flask", request: "Request"
    ) -> t.Optional[SecureCookieSession]:
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        val = request.cookies.get(self.get_cookie_name(app))
        if not val:
            return self.session_class()
        max_age = int(app.permanent_session_lifetime.total_seconds())
        try:
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
            return self.session_class()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    # 从 cookie 中取出字符串
    val = request.cookies.get(self.get_cookie_name(app))
    # get_cookie_name 获取的是 SESSION_COOKIE_NAME 默认配置的 session 键名,可以修改
    
    # 反序列化成 session 对象
    data = s.loads(val, max_age=max_age)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. save_session

    save_session 方法请求走了,把 session 对象,序列化成字符串,放到 cookie 中

    def save_session(
        self, app: "Flask", session: SessionMixin, response: "Response"
    ) -> None:
        name = self.get_cookie_name(app)
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)
        secure = self.get_cookie_secure(app)
        samesite = self.get_cookie_samesite(app)
        
        if not session:
            if session.modified:
                response.delete_cookie(
                    name, domain=domain, path=path, secure=secure, samesite=samesite
                )
            return
            
        if session.accessed:
            response.vary.add("Cookie")
        if not self.should_set_cookie(app, session):
            return
    
        httponly = self.get_cookie_httponly(app)
        expires = self.get_expiration_time(app, session)
        val = self.get_signing_serializer(app).dumps(dict(session))  # type: ignore
        # 设置 cookie 
        response.set_cookie(
            name,
            val,  # type: ignore
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite,
        )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    1. app.session_interface 中 save_session 的参数(设置cookie的参数)
    参数说明
    key
    value
    max_age=None超时时间 cookie需要延续的时间(以秒为单位)如果参数是 None ,这个cookie会延续到浏览器关闭为止
    expires=None超时时间(IE requires expires, so set it if hasn’t been already.)
    path=‘/’Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。
    domain=NoneCookie生效的域名 你可用这个参数来构造一个跨站cookie。如, domain=".example.com"所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取
    secure=False浏览器将通过HTTPS来回传cookie
    httponly=False只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)

    9. 闪现 message

    闪现是由 flash 翻译过来的。一般用在当 a 页面操作出错,跳转到 b 页面,在 b 页面显示 a 页面的错误信息。其本质就是放到session中,等同于 django 的 message 框架

    基础使用

    设置: flash()
    取值: get_flashed_message()
    
    • 1
    • 2
    from flask import Flask, flash, get_flashed_messages, redirect
    
    app = Flask(__name__)
    app.secret_key = 'asdfasdf'
    app.debug = True
    
    
    @app.route('/index')
    def index():
    	# flash('超时错误')
    	# 添加分类
        flash('超时错误', category="x1")
        return redirect('/error')
    
    @app.route('/error')
    def error():
        """
        展示错误信息
        :return:
        如果get_flashed_messages(with_category=True)
        """
        # msg = get_flashed_messages()
        # 添加分类,取出的值是列表
        msg = get_flashed_messages(category_filter=['x1'])    
        return "错误信息:%s" % msg[0]
    
    if __name__ == '__main__':
        app.run()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    注意点: 在一次请求中,把一些数据放在闪现中,下次请求就可以从闪现中取出来,取一次就没了(使用分类也是这样)

    10. 请求扩展

    10.1 before_request

    类比 django 中间件中的 process_request,在请求收到之前绑定一个函数做一些事情
    若其返回的是响应对象,则不继续往下走了,如果返回 None,继续走下一个请求扩展

    • 请求来了,就会执行它
    • 多个 before_request,会从上往下依次执行
    # 基于它做用户登录认证
    @app.before_request
    def process_request(*args, **kwargs):
        if request.path == '/login':
            return None
        try:
            session['user_info']
            return None
        except KeyError as e:
            return redirect('/login')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    10.2 after_request

    类比 django 中间件中的 process_response,每一个请求之后绑定一个函数,如果请求没有异常

    • 必须返回 response 对象
    • 请求走了,会执行它
    • 多个 after_request,从下往上依次执行
    @app.after_request
    def p1(response):
        print(111)
        print(response)
        return response
    
    
    @app.after_request
    def p2(response):
        print(222)
        print(response)
        return response
    
    
    输出:
    >> 222
    >> <Response 5 bytes [200 OK]>
    >> 111
    >> <Response 5 bytes [200 OK]>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    10.3 before_first_request

    启动后第一次请求时,跟浏览器无关。返回 None,会继续往下执行

    @app.before_first_request
    def first():
        pass
    
    • 1
    • 2
    • 3

    10.4 teardown_request

    每一个请求之后绑定一个函数,即使遇到了异常。无论程序是否出异常,都会触发它,可以用来记录错误日志

    @app.teardown_request
    def ter(e):
    	# 打印的错误信息,注意需要将 debug 设置为 False
        print(e)
        return None
    
    • 1
    • 2
    • 3
    • 4
    • 5

    10.5 errorhandler

    监听某个状态码,如果是这个状态码的错误,就会触发它的执行。可以返回响应对象例如 404 界面

    @app.errorhandler(404)
    def error_404(e):
    	# 错误信息
        print(e)
        return '该页面不存在'
    
    • 1
    • 2
    • 3
    • 4
    • 5

    10.6 template_global

    标签

    @app.template_global()
    def add(a, b):
        return a + b
    
    # 在模板中使用 {{add(1,2)}}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    10.7 template_filter

    过滤器

    @app.template_filter()
    def add(a, b):
        return a + b
    
    # 在模板中使用 {{3|add(1)}}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    10.8 总结

    1 重点掌握before_request和after_request,

    2 注意有多个的情况,执行顺序

    3 before_request请求拦截后(也就是有return值),response所有都执行

    @app.before_request
    def req1():
        print('这是请求1')
        return '这是请求1'
    
    @app.before_request
    def req2():
        print('这是请求2')
        return '这是请求2'
    
    @app.after_request
    def res1(response):
        print('这是响应1')
        return response
    
    @app.after_request
    def res2(response):
        print('这是响应2')
        return response
    
    
    输出:
    >> 这是请求1
    >> 这是响应2
    >> 这是响应1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    11. 蓝图

    蓝图是由 Blue_print 翻译过来的,作用是对目录进行划分

    11.1 使用蓝图之中小型系统

    目录结构:

    flaskProject					# 项目名
    	flaskProject				# 包名
    		static					# 静态文件夹
    		templates				# 模板文件夹
    		views					# 视图函数文件夹
    			user.py				# 用户相关视图函数
    			goods.py			# 商品相关视图函数
    		__init__.py				# 包 init 文件
    	run.py						# 项目启动文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • _init_
    from flask import Flask
    from flaskProject.views import user
    from flaskProject.views import goods
    
    # 注册app,指定模板文件路径、静态文件路径以及路径后缀
    app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static')
    
    # 在app中注册蓝图
    app.register_blueprint(user.user)
    app.register_blueprint(goods.good)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • user.py
    from flask import Blueprint
    
    user = Blueprint('user', __name__)
    
    # 使用蓝图注册路由
    @user.route('/login')
    def login():
        return 'login'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • run.py
    from flaskProject import app
    
    if __name__ == '__main__':
    	# 指定运行的IP与端口
        app.run(host='0.0.0.0', port=8080)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    11.2 使用蓝图之大型系统

    目录结构:

    flaskProject_big						# 项目名
    	flaskProject_big					# 包名 
    		goods							# app 名
    			static						# app 独立静态文件
    			templates					# app 独立模板
    			__init__.py					# app 双下 init
    			views.py					# app 独立视图函数
    			
    		user							# app 名
    			static						# app 独立静态文件
    			templates					# app 独立模板
    			__init__.py					# app 双下 init
    			views.py					# app 独立视图函数
    			
    		__init__.py						# 包 双下init
    	run.py								# 启动文件
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • flaskProject_big/user/init.py
    from flask import Blueprint
    
    # 第一步:初始化蓝图
    user = Blueprint(
        'user',
        __name__,
        template_folder='templates',  # 指定该蓝图对象自己的模板文件路径
        static_folder='static',  	  # 指定该蓝图对象自己的静态文件路径
        static_url_path='/static',
    )
    
    # 导入视图后路由才能被匹配,且不能放在前面导入
    from . import views
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • flaskProject_big/user/views.py
    # 导入蓝图
    from . import user
    from flask import render_template
    
    @user.before_request
    def before():
        print('user 的 before')
    
    # 第三步:使用蓝图注册路由
    @user.route('/login')
    def login():
        return render_template('login.html')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • flaskProject_big/user/templates/login.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    <body>
    <img src="static/m.jpeg">
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • flaskProject_big/init.py
    from flask import Flask
    from .goods import goods
    from .user import user
    
    app = Flask(__name__)
    app.debug = True
    
    # 第二步:在app中注册蓝图
    # url_prefix='/user' 设置访问这个app的前缀,等同于django include
    app.register_blueprint(user, url_prefix='/user')  
    app.register_blueprint(goods)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • run.py
    from flaskProject_big import app
    
    if __name__ == '__main__':
        app.run(port=8080)
    
    • 1
    • 2
    • 3
    • 4

    总结:

    • 第一步,在 app 的双下 init 中初始化蓝图,并导入视图
    • 第二步,在 包 的双下 init 中注册蓝图,可以设置 url_prefix 路由前缀
    • 第三步,在 app 的视图文件中注册路由,编写视图函数即可

    注意点:

    • 加了 url_prefix (蓝图URL前缀)后,在该蓝图下所有 url 都加前缀
    • 初始化蓝图时,template_folder 给当前蓝图单独使用 templates,若当前找不到,会向上查找,找总templates
    • 蓝图的请求拓展例如 befort_request,只对当前蓝图有效
    • 大型项目,可以模拟出类似于 django 中 app 的概念

    12. g 对象

    专门用来存储用户信息的g对象,g的全称的为global。不放在 request中是为了防止值被覆盖掉

    # 请求拓展
    @user.before_request
    def before():
        g.name = 'xwx'
    
    @user.route('/login')
    def login():
        print(g.name, type(g))
        return render_template('login.html')
    
    输出:
    >> xwx <class 'werkzeug.local.LocalProxy'>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    g 对象和 session 的区别

    • session 对象是可以跨 request 的,只要 session 还未失效,不同的 request 的请求会获取到同一个 session
    • 但是 g 对象不是,g 对象不需要管过期时间,请求一次就 g 对象就改变了一次,或者重新赋值了一次,也就是 g 对象只对当次请求有效

    13. flask-session

    session 默认以 cookie 的形式放到浏览器中,若把 session 放到服务端保存( redis 、mysql 中等等)

    作用:将默认保存的签名 cookie 中的值保存到 redis/memcached/file/Mongodb/SQLAlchemy
    安装:pip3 install flask-session

    Django 存放到 redist 参考:https://blog.csdn.net/weixin_42194215/article/details/111414823

    13.1 使用方式一

    from flask import Flask,session
    from flask_session import RedisSessionInterface
    import redis
    
    app = Flask(__name__)
    
    conn=redis.Redis(host='127.0.0.1',port=6379)
    # key_prefix 表示在 redis 的前缀
    app.session_interface=RedisSessionInterface(conn,key_prefix='xwx_')
    
    @app.route('/')
    def hello_world():
        session['name']='xwx'
        return 'Hello World!'
    
    if __name__ == '__main__':
        app.run()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    13.2 使用的方式二

    from redis import Redis
    from flask_session import Session
    app.config['SESSION_TYPE'] = 'redis'
    app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
    # 用类包裹,常见写法
    Session(app)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    问题:设置cookie时,如何设定关闭浏览器则cookie失效。

    response.set_cookie('k','v',exipre=None)#这样设置即可
    #在session中设置
    app.session_interface=RedisSessionInterface(conn,key_prefix='lqz',permanent=False)
    #一般不用,我们一般都设置超时时间,多长时间后失效
    
    • 1
    • 2
    • 3
    • 4

    问题:cookie默认超时时间是多少?如何设置超时时间

    #源码expires = self.get_expiration_time(app, session)
    'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),#这个配置文件控制
    
    • 1
    • 2
  • 相关阅读:
    Android13 新增 Stable AIDL接口
    PyTorch图文安装教程(Win10),含遇到的问题及解决办法
    博客网页制作基础大二dw作业 web课程设计网页制作 个人网页设计与实现 我的个人博客网页开发
    使用BigDecimal的坑
    【架构设计】.Net Core负载均衡实现
    【短文】Linux怎么读取文件大小
    深度对话|Sui在商业技术堆栈中的地位
    C# 泛型详解(泛型类,方法,接口,委托,约束,反射 )
    用GAN网络生成彩票号码
    ECCV 2022 旷视入选论文亮点解读(上)
  • 原文地址:https://blog.csdn.net/m0_58987515/article/details/126181294