Flask的路由、蓝图和装饰器

复习装饰器

基于session编写登录验证的装饰器。

- 思考:如何给两个以上的视图函数增加装饰器

def is_login(func):
    def inner(*args, **kwargs):
        if session.get('username'):
            ret = func(*args, **kwargs)
        else:
            return redirect('/login')
        return ret

    return inner

测试给两个以上的视图函数增加装饰器出现的报错信息

"AssertionError: View function mapping is overwriting an existing endpoint function: inner"

# 百度翻译
# 主动抛出异常, 视图函数存在重名
"断言错误:视图函数映射覆盖现有终结点函数:内部"

解决方法1:

把 inner 改掉,保留原始的函数名字,这样返回的不是相同的 inner 了。 ---  functools.wraps(func)

from functools import wraps

def is_login(func):
    @wraps(func)
    def inner(*args, **kwargs):
        if session.get('username'):
            ret = func(*args, **kwargs)
        else:
            return redirect('/login')
        return ret

    return inner

解决方法2:

加入endpoint,在对应mapping映射的时候,不会出现视图函数重名。 ---  endpoint: 路由关系映射, 别名

def is_login(func):
    def inner(*args, **kwargs):
        if session.get('username'):
            ret = func(*args, **kwargs)
        else:
            return redirect('/login')
        return ret

    return inner

@app.route('/home', endpoint="home")
@is_login
def home():
    return render_template('home.html', sd=STUDENT_DICT)

@app.route('/student', endpoint="student")
@is_login
def student():
    id = request.args.get('id')
    student = STUDENT_DICT.get(int(id), None)
    return render_template('student.html', student=student, id=id)

if __name__ == '__main__':
    app.run('0.0.0.0', 9999)

Flask中的路由

endpoint (映射路由-视图函数)

是用来映射的,可以路由映射视图函数,当然也可以视图函数映射路由。

视图函数__name__ = " " 可通过url_for("endpoint的值")方法获取到请求路径(反向解析)

# 测试,导入url_for
from flask import (Flask, request, render_template, redirect, session, url_for)

@app.route('/login', methods=['POST', 'GET'], endpoint='loginxxx')
def login():
    print(url_for("loginxxx"))
    if request.method == "GET":
        return render_template('login.html')
    else:
        username = request.form.get('username')
        password = request.form.get('pwd')
        if username == '123' and password == "123":
            session["username"] = username
            return render_template('home.html', sd=STUDENT_DICT)
        else:
            return redirect('/login')

# 运行结果:/login

methods

可迭代对象,元组、列表都可以

例如:

methods = ["get","Post"] 
# 当前视图函数支持的请求方法
# 源码中会有upper,所以大小写无所谓。

如果请求方法不在methods里面会抛出HTTP的状态码:405 请求方式不被允许

defaults(默认参数,不常用)

@app.route('/login', methods=['POST', 'GET'], endpoint='loginxxx', defaults={'id':5})
def login(id):
    print(url_for("loginxxx"),id)
    if request.method == "GET":
        return render_template('login.html')
    else:
        username = request.form.get('username')
        password = request.form.get('pwd')
        if username == '123' and password == "123":
            session["username"] = username
            return render_template('home.html', sd=STUDENT_DICT)
        else:
            return redirect('/login')

# 结果: /login 5

注意事项:

一旦默认参数存在,视图函数中必须有一个形参去接收,形参变量名必须与 defaults 中定义的一致。

strict_slashes=True

是否严格遵循路由匹配规则 "/",默认是True

@app.route('/login', methods=['POST', 'GET'], endpoint='loginxxx',strict_slashes=False)
def login():
    print(url_for("loginxxx"))
    if request.method == "GET":
        return render_template('login.html')
    else:
        username = request.form.get('username')
        password = request.form.get('pwd')
        if username == '123' and password == "123":
            session["username"] = username
            return render_template('home.html', sd=STUDENT_DICT)
        else:
            return redirect('/login')

# 更改成False后,加上/也是可以访问的。

redirect_to

redirect_to = "/login" 
# 永久重定向 301 308 不经过视图函数的

动态参数路由(重点)

@app.route("/login/<int:page>") 指定int类型 /login/1

@app.route("/login/<floder>/<filename>") 默认是str类型 /login/templates/index.html

分页的场景:

@app.route('/home/<int:page>', endpoint="home")
@is_login
def home(page):
    print(page)
    return render_template('home.html', sd=STUDENT_DICT)

输入什么数字,便会在控制台打印出来,也可以多个(加多个<int:page1>/<int:page2>)等等,不过必须要是定长的。

不过是有问题的,如果输入0的时候不小心输入了o,那就会报错,解决方法,更改成string就行,默认就是string,string也是包含int的。

什么都不写,也是string的。

@app.route('/home/<page>', endpoint="home")
@is_login
def home(page):
    print(page)
    return render_template('home.html', sd=STUDENT_DICT)

另一种:

from flask import (Flask, request, render_template, redirect, session, url_for, send_file)

@app.route('/home/<filename>', endpoint="home")
@is_login
def home(filename):
    return send_file(filename)

Flask中的配置

1.初始化配置

- app = Flask(__name__, template_folder="")

- template_folder="" 更改模板存放目录, 默认值templates

- static_folder="" 静态文件存放路径 - 默认是/static

- static_url_path="" 静态文件访问路径 - 默认是 "/"+static_folder

-- 静态文件存放路径: 存放静态文件的实际路径, 根据该路径寻找服务器中存放的相应静态文件

-- 静态文件访问路径: 相当于静态文件存放路径的别名, 只要请求该访问路径即相当于在静态文件的存放路径下查找资源

2.Config 对象配置

- app.config["DEBUG"] = True  和 app.debug = True 效果一样。

- app.default_config 查看所有配置

- DEBUG --- 编码阶段 代码重启 日志输出级别很低 页面中会显示错误 错误会展示代码暴露后台信息

- TESTING --- 测试阶段 日志输出级别较高 无限接近线上环境

- app.config[JSON_MIMETYPE] = 1231/21312

app.default_config 查看所有配置

config中有用的key

{
    'DEBUG': False,  # 是否开启Debug模式
    'TESTING': False,  # 是否开启测试模式
    'PROPAGATE_EXCEPTIONS': None,  # 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True
    'PRESERVE_CONTEXT_ON_EXCEPTION': None,  # 一两句话说不清楚,一般不用它
    'SECRET_KEY': None,  # 之前遇到过,在启用Session的时候,一定要有它
    'PERMANENT_SESSION_LIFETIME': 31,  # days , Session的生命周期(天)默认31天
    'USE_X_SENDFILE': False,  # 是否弃用 x_sendfile
    'LOGGER_NAME': None,  # 日志记录器的名称
    'LOGGER_HANDLER_POLICY': 'always',
    'SERVER_NAME': None,  # 服务访问域名
    'APPLICATION_ROOT': None,  # 项目的完整路径
    'SESSION_COOKIE_NAME': 'session',  # 在cookies中存放session加密字符串的名字
    'SESSION_COOKIE_DOMAIN': None,  # 在哪个域名下会产生session记录在cookies中
    'SESSION_COOKIE_PATH': None,  # cookies的路径
    'SESSION_COOKIE_HTTPONLY': True,  # 控制 cookie 是否应被设置 httponly 的标志,
    'SESSION_COOKIE_SECURE': False,  # 控制 cookie 是否应被设置安全标志
    'SESSION_REFRESH_EACH_REQUEST': True,  # 这个标志控制永久会话如何刷新
    'MAX_CONTENT_LENGTH': None,  # 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码
    'SEND_FILE_MAX_AGE_DEFAULT': 12,  # hours 默认缓存控制的最大期限
    'TRAP_BAD_REQUEST_ERRORS': False,
    # 如果这个值被设置为 True ,Flask不会执行 HTTP 异常的错误处理,而是像对待其它异常一样,
    # 通过异常栈让它冒泡地抛出。这对于需要找出 HTTP 异常源头的可怕调试情形是有用的。
    'TRAP_HTTP_EXCEPTIONS': False,
    # Werkzeug 处理请求中的特定数据的内部数据结构会抛出同样也是“错误的请求”异常的特殊的 key errors 。
    # 同样地,为了保持一致,许多操作可以显式地抛出 BadRequest 异常。
    # 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。
    # 如果这个值被设置为 True ,你只会得到常规的回溯。
    'EXPLAIN_TEMPLATE_LOADING': False,
    'PREFERRED_URL_SCHEME': 'http',  # 生成URL的时候如果没有可用的 URL 模式话将使用这个值
    'JSON_AS_ASCII': True,
    # 默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False ,
    # Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。
    # 比如 jsonfiy 会自动地采用 utf-8 来编码它然后才进行传输。
    'JSON_SORT_KEYS': True,
    #默认情况下 Flask 按照 JSON 对象的键的顺序来序来序列化它。
    # 这样做是为了确保键的顺序不会受到字典的哈希种子的影响,从而返回的值每次都是一致的,不会造成无用的额外 HTTP 缓存。
    # 你可以通过修改这个配置的值来覆盖默认的操作。但这是不被推荐的做法因为这个默认的行为可能会给你在性能的代价上带来改善。
    'JSONIFY_PRETTYPRINT_REGULAR': True,
    'JSONIFY_MIMETYPE': 'application/json',
    'TEMPLATES_AUTO_RELOAD': None,
}

以上这些Key,都可以被改写,当然他们也都是有默认值存在的,如果没有特殊情况,不要改写它的默认值

修改配置的方式大约是两种

1.直接对app.config进行修改

app.config["DEBUG"] = True

2.setting文件快速配置DEBUG与TESTING

config的特性

app.config.from_object() 写入的类中定义了什么就添加什么
class Debugsetting(object):
    DEBUG = True

具体配置:

1.在项目路径下新建settings.py文件, 在文件中编写 class
class DebugSettings(object):
    DEBUG = True
    SECRET_KEY = "123456"
    SESSION_COOKIE_NAME = "I am debug session"
class TestingSettings(object):
    TESTING = True
    SECRET_KEY = "!@#$%^&*()_"
    SESSION_COOKIE_NAME = "I am Not session"

2.在启动文件导入class
    from settings import DebugSettings
    from settings import TestingSettings

3.调用生效
    app.config.from_object(DebugSettings)
    # app.config.from_object(TestingSettings)

使用哪个就调用哪个,不使用注释掉即可

Flask 蓝图 Blueprint

- 当作是一个不能够被run的Flask对象(这样理解存在偏差),也没有config所以不能run

- 应用隔离

- 蓝图中不存在config

- 蓝图需要注册在app实例上app.register_blueprint()

-- url_prefix="" 路由前缀

 test:

1.创建一个新的目录blue, 在目录下创建文件user.py
2.编辑user.py
    from flask import Blueprint
        user= Blueprint("user", __name__, url_prefix="/blue")
        @user.route("/index")
        def index():
            return "blue_index_user"
3.在启动文件中导入Blueprint实例user
    from blue.user import user
4.注册实例
    app.register_blueprint(user)
5.浏览器访问地址
    http://192.168.16.35:9999/index

Flask 特殊装饰器(类似中间件)

1.@app.before_request 请求进入视图函数之前进行处理

return None 继续执行, 否则阻断
@app.before_request  # 请求进入视图函数之前
def before1():
    print('be1')
    return None

# 登录验证
@home.before_request
def is_login():
    if not session.get('username'):
        return redirect('/login')

2.@app.after_request 视图函数结束, 响应客户端之前

正常周期: beforef1 -> beforef2 -> vf -> afterf2 -> afterf1

异常周期: beforef1 -> afterf2 -> afterf1

3.errorhandler 重定义错误信息

1.有参数的装饰器errorhandler(监听错误状态码) 必须是int类型

2.所装饰的函数必须有一个形参来接收errormessage

@app.errorhandler(404)
def error404(error_msg):
    print(error_msg)
    return "你要访问的页面不存在"

偷懒一下,使用redirect

@app.errorhandler(404)
def error404(error_msg):
    print(error_msg)
    return redirect('https://www.bilibili.com/dsadsad')

使用send_file,可以传个图片,视频都是可以的哦

@app.errorhandler(404)
def error404(error_msg):
    print(error_msg)
    return send_file('桌面.png')

注意:监听错误状态码 5xx 4xx ,是int类型的。200啦,还有302啥的都不可以!!!

错误信息总结

1. 蓝图实例与被装饰的函数不可重名

AttributeError: 'function' object has no attribute 'name'

2.两个文件之间循环导入

ImportError: cannot import name 'stu'

 

  

 

posted @ 2019-07-11 15:57  blog_wu  阅读(569)  评论(0编辑  收藏  举报