flask 路由、钩子函数

一、路由

1、常用路由参数:

flask的路由是基于装饰器的

- rule:路径,不能写正则

- methods=['GET','POST] : 允许的请求方式

- endpoint: 当前路由的别名---》反向解析用

- defaults:用于给 URL 路径中的参数提供默认值。这对于构建更具灵活性和容错性的 URL 路径规则非常有用

- redirect_to: 用于在访问某个 URL 路径时,自动重定向到另一条路径。它可以是一个字符串,或者是一个返回重定向目标的可调用对象。

from flask import Flask, redirect, url_for

app = Flask(__name__)

@app.route('/new_user_path')
def new_user_path():
    return "You've been redirected to the new user path."

@app.route('/old-user-path', redirect_to='new_user_path')
def redirect_old_path():
    return '页面跳转'

@app.route('/user/<username>/page', defaults={'page': 1}, methods=['GET', 'POST'], endpoint='user_profile_default')
@app.route('/user/<username>/page/<int:page>', methods=['GET', 'POST'], endpoint='user_profile')
def show_user(username, page):
    return f"User: {username}, Page: {page}"

@app.route('/home')
def home():
    return redirect(url_for('user_profile', username='zjz', page=1))

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

补充:

1.1  一个视图函数可以有多个路由装饰器

1.2 使用 redirect_toredirect 和 url_for 可以处理 URL 重定向和生成

  redirect_to 是一个用于路由装饰器中的快速重定向,通常用于将一个旧路径重定向到新的路径。这是在路由装饰器中指定的,而不是在函数实现中

  redirect 是一个函数,通常在路由对应的视图函数内部使用,用于将用户请求重定向到新的 URL。

from flask import redirect

@app.route('/some-path')
def some_function():
    return redirect('/another-path')

  url_for 是一个用于生成 URL 的函数,它可以根据 Flask 应用中的路由定义生成外部 URL,非常适用于生成动态 URL,尤其是当路由的 URL 结构可能改变时。

from flask import url_for

@app.route('/user/<username>/page/<int:page>')
def show_user(username, page):
    return f"User: {username}, Page: {page}"

@app.route('/get-url')
def get_url():
    # 使用 url_for 生成 show_user 的 URL
    user_url = url_for('show_user', username='john', page=2)
    return redirect(user_url)
  • **redirect_to**:主要用在路由装饰器中,它是一个快捷方式,用于将一个 URL 重定向到另一个指定的视图函数或 URL。
  • **redirect**:用在视图函数内部,以编程方式将请求重定向到一个新的 URL。
  • **url_for**:用于生成 URL,基于 URL 路由,确保生成的 URL 始终与定义的路由匹配,即使路由改变也能自动适配。

2、路由转换器

string

(默认)接受没有斜线的任何文本

int

接受正整数

float

接受正浮点值

path

类似 string ,但可以包含斜杠

uuid

接受UUID字符串

用例

from flask import Flask

app = Flask(__name__)


@app.route('/hello', methods=['GET'])
def hello():
    return "Hello, World!"


@app.route('/user/<username>', methods=['GET'])
def show_user_profile(username):
    return f'User: {username}'


@app.route('/post/<int:post_id>', methods=['GET'])
def show_post(post_id):
    return f'Post ID: {post_id}'


@app.route('/file/<path:filename>', methods=['GET'])
def show_file(filename):
    return f'File: {filename}'


@app.route('/uuid/<uuid:uid>', methods=['GET'])
def show_uuid(uid):
    return f'UUID: {uid}'


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

浏览器访问测试:

int转换器

 

file 转换器

uuid 转换器

3、其他写法

@app.route  + methods  不写methods 默认只有get
    -@app.get
    -@app.post

4、自己注册路由不使用装饰器

app.add_url_rule('/', view_func=hello)

from flask import Flask

app = Flask(__name__)


@app.route('/hello', methods=['GET'])
def hello():
    return "Hello, Jingzhiz!"


app.add_url_rule('/', view_func=hello)

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

补充:路由系统本质

-装饰器---> 本质是 self.add_url_rule(rule, endpoint, f, **options)

-self是 Flask(__name__) app对象

源码如下:

    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

    @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、@app.before_request 和 @app.after_request

from flask import Flask

app = Flask(__name__)


@app.before_request
def before1():
    print('before1')


@app.before_request
def before2():
    print('before2')
    return '第二个返回了'


@app.before_request
def before3():
    print('before3')


@app.after_request
def after1(response):
    print('after1')
    return response


@app.after_request
def after2(response):
    print('after2')
    return response


@app.after_request
def after3(response):
    print('after3')
    return response


@app.route('/')
def index():  # put application's code here
    return '返回了'


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

结果 :

before_request: 一旦返回了有效值,则后续的 @app.before_request 和视图函数将不在执行,这里 before3() 和  index() 不再执行

after_request:在视图函数处理完成并生成响应后,从下往上依次执行所有的 after_request 函数。这个流程不会中断,所以所有的 after_request 函数都会执行。

2、before_request 的使用场景

before_request 钩子函数在每个请求到达视图函数之前执行。这允许在正式处理请求之前进行一些预处理操作。以下是一些常见的使用场景:

2.1 用户认证与授权:

  在访问任何受保护的路由之前检查用户是否已登录。

  校验用户是否有权限访问特定资源。

@app.before_request
def check_authentication():
    if not is_user_authenticated():
        return redirect('/login')

2.2  初始化上下文和资源:

在每个请求开始时,初始化一些全局变量、数据库连接或其他资源。

@app.before_request
def initialize_globals():
    g.some_data = load_some_data()

2.3 记录请求的信息用于审计或分析

@app.before_request
def log_request_info():
    print(f'Handling request for {request.path}')

2.4 防止CSRF攻击:

验证请求中的CSRF令牌,确保请求是从可信来源发出的。

@app.before_request
def protect_from_csrf():
    if request.method == "POST":
        token = request.form.get('csrf_token')
        if not token or token != session.get('csrf_token'):
            abort(403)

3、after_request 的使用场景

after_request 钩子函数在视图函数处理完请求后执行,允许对响应进行附加处理。这可以用来添加全局的后处理逻辑。以下是一些常见的使用场景:

3.1 修改或增强响应:

  添加或修改HTTP头

  增加默认的响应格式,如增加跨域资源共享(CORS)头

@app.after_request
def add_cors_headers(response):
    response.headers['Access-Control-Allow-Origin'] = '*'
    return response

3.2 记录响应日志:

记录响应的相关信息,用于监控或分析。

@app.after_request
def log_response_info(response):
    print(f'Returned status: {response.status}')
    return response

3.3  缓存控制:

修改响应头部以控制缓存策略

@app.after_request
def add_cache_control(response):
    if request.endpoint == 'static':
        response.headers['Cache-Control'] = 'public, max-age=31536000'
    return response

3.4  清理资源:

请求结束后进行资源的清理或释放,例如关闭数据库连接。

@app.after_request
def release_resources(response):
    close_database_connection()
    return response

3.5 安全增强:

增加安全相关的头部,如内容安全政策(Content Security Policy, CSP)。

@app.after_request
def set_security_headers(response):
    response.headers['Content-Security-Policy'] = "default-src 'self'"
    return response

4、@app.teardown_request  

@app.teardown_request
def jingzhiz(err):
    print(err)  # 每一个请求之后绑定一个函数,即使遇到了异常。如果视图函数正常顺利运行,err是None的,如果视图函数出错了,err就是错误对象,一般用来做日志记录
    print('teardown_request')

@app.after_request 和 @app.teardown_request

总结对比

特性@app.after_request@app.teardown_request
调用时机 请求处理完成后、响应发送前 请求结束后,无论成功与否
返回值 必须返回一个响应对象 返回 None(无需返回响应)
用途 修改响应对象或添加自定义响应头 清理资源、处理请求期间的错误
是否与异常相关 与异常无关 可以处理请求过程中发生的异常

适用场景

  • 使用 @app.after_request 当你需要修改响应,或设置响应头时。
  • 使用 @app.teardown_request 当你需要进行资源清理或错误处理时。

5、@app.errorhandler

@app.errorhandler(404)
def error(arg):
    print('404测试', arg)
    return render_template('404.html')

6、@app.template_global() 和  @app.template_filter() 

是Flask中用于模板处理的两个装饰器,它们可以让你在Jinja2模板中定义全局变量和自定义过滤器,帮助你更灵活地处理模板中的数据。

6.1 @app.template_global() 
用于在Flask应用的模板环境中注册一个全局函数。这个函数可以在前端模板中直接调用,类似于Python中的内置函数 
定义全局函数:在模板中定义一些常用的、全局可用的函数。
from flask import Flask

app = Flask(__name__)

@app.template_global()
def greet(name):
    return f"Hello, {name}!"

@app.route('/')
def index():
    return render_template('index.html')

在html 页面中使用变量

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ greet('World') }}    <!-- 输出:Hello, World! -->
</body>
</html> 

6.2 @app.template_filter() 用于创建自定义的Jinja2过滤器。这些过滤器可以在模板中用于格式化数据,类似于内置的过滤器(如 str.upper)。

from flask import Flask

app = Flask(__name__)

@app.template_filter()
def reverse(s):
    return s[::-1]

@app.route('/')
def index():
    return render_template('index.html')

在 index.html 模板中,该自定义过滤器可以在管道操作中使用:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ "Hello, World!" | reverse }}    <!-- 输出:!dlroW ,olleH -->
</body>
</html>
  • **@app.template_global()**:用于注册全局函数,便于模板随时调用。适合逻辑较复杂且在多处地方使用的操作。
  • **@app.template_filter()**:用于注册自定义过滤器,在模板中对数据进行处理。适用于需要在模板中重复使用的格式化操作。

 

posted @ 2024-09-14 16:52  凡人半睁眼  阅读(9)  评论(0编辑  收藏  举报