内容概述:
to_python、to_url,
异常捕获,
请求钩子,
获取请求参数,
状态保持,
上下文,
Flask-Script 扩展,
模板
to_python、to_url
自定义转换器
to_python: 可以对匹配到的参数进行处理并返回,在调用视图函数前执行
to_url :在使用 url_for 的时候,对视图函数传的参数进行处理,处理完毕之后以便能够进行路由匹配
异常捕获
HTTP异常主动抛出
abort 方法:抛出一个给定状态代码的 HTTPException 或者 指定响应,例如想要用一个页面未找到异常来终止请求,你可以调用 abort(404)。
参数:code——HTTP 的错误状态码
捕获错误
errorhandler 装饰器 :注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
参数: code_or_exception – HTTP的错误状态码或指定异常
from flask import Flask
from flask import abort
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/demo1')
def demo1():
# 主动抛出 HTTP 指定错误代码
# abort(404)
a = 0
b = 1 / a
return 'demo1'
@app.errorhandler(404)
def page_not_found(error):
return "页面不见了~"
@app.errorhandler(ZeroDivisionError)
def zero_division_error(error):
return "除数不能为0 "
if __name__ == '__main__':
app.run(debug=True)
请求钩子
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理 ,为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子 .
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
before_first_request: 在第一次请求之前会执行
before_request : 在每次请求之前会执行,可以在这里对一些非法的请求进行阻止,那么视图函数将不再被调用
after_request: 在请求之后会执行,并且函数里面接收一个参数:响应,还需要将响应进行返回
teardown_request: 在请求之后会执行,如果请求的函数有异常,会把具体异常抛出
from flask import Flask
app = Flask(__name__)
@app.before_first_request
def before_first_request():
"""在第一次请求之前会访问该函数"""
print('before_first_request')
@app.before_request
def before_request():
"""在每次请求之前会访问该函数"""
print('before_request')
# 可以对一些非法的请求进行阻止
# if 如果ip在黑名单:
# return "在黑名单中"
@app.after_request
def after_request(response):
"""在请求之后会调用,并且函数里面接收一个参数:响应,还需要将响应进行返回"""
print('after_request')
return response
@app.teardown_request
def teardown_request(error):
"""在请求之后会执行,如果请求的函数报有异常,会把具体异常传入到此函数"""
print('teardown_request')
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
装饰器路由的实现
Flask有两大核心:Werkzeug和Jinja2
- Werkzeug实现路由、调试和Web服务器网关接口 - Jinja2实现了模板。
获取请求参数
request:
request 就是flask中代表当前请求的 request 对象,其中一个请求上下文变量(理解成全局变量,在视图函数中直接使用可以取到当前本次请求)
常用的属性如下:
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求的数据,并转换为字符串 | * |
form | 记录请求中的表单数据 | MultiDict |
args | 记录请求中的查询参数 | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的报文头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件 | * |
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
# OSError: [Errno 98] Address already in use
# 端口被占用,是程序没有完全关闭,后台还在运行
# 在 ubuntu 中使用命令: netstat -apn | grep 5000 找到 5000 端口对应的 pid
# 使用 kill -9 pid 命令进行清除
@app.route('/upload', methods=['POST'])
def upload():
file = request.files.get('pic')
file.save('aaa.png')
return 'success'
@app.route('/data', methods=['POST'])
def data():
data = request.data
print(data)
return 'ok'
if __name__ == '__main__':
app.run(debug=True)
状态保持
- http 是一种无状态协议,浏览器请求服务器是无状态的。
- 无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。
- 无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且服务器也会在处理页面完毕之后销毁页面对象。
- 实现状态保持主要有两种方式:
- 在客户端存储信息使用
Cookie
- 在服务器端存储信息使用
Session
无状态协议:
- 协议对于事务处理没有记忆能力
- 对同一个 url 请求没有上下文关系
- 每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况
- 服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器
- 人生若只如初见
cookie
- Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用
- Cookie基于域名安全,不同域名的Cookie是不能互相访问的
- 浏览器的同源策略,所谓同源是指,域名,协议,端口相同。不同源的客户端脚(javascript、ActionScript)本在没明确授权的情况下,不能读写对方的资源。
make_response 获取 response 对象
set_cookie(‘username’, ‘xiaohei’, max_age=3600) 设置cookie,设置过期时间
request.cookies.get() 获取cookie
cookie 流程:
设置 cookie: 通过响应带给浏览器
获取 cookie:从请求中获取
- 浏览器发送登录请求,发送请求报文,带上要登录的账号和密码
- 服务器校验账号和密码
- 如果正确就给出响应,带上设置好的 cookie ,
response.set_cookie('username', 'xiaohei')
- 浏览器会自动将 cookie 保存起来
- 下次请求该网站时,会把 cookie 请求带到服务器,服务器取到传过来的 cookie,根据 cookie 就可以知道是否登录,是谁登录
from flask import Flask
from flask import make_response
from flask import request
app = Flask(__name__)
@app.route('/')
def index():
user_id = request.cookies.get('user_id')
user_name = request.cookies.get('user_name')
return "%s-----%s" % (user_id, user_name)
@app.route('/login')
def login():
# 默认判断账号与密码是正确的
response = make_response('success')
# 设置 cookie
response.set_cookie('user_id', '1', max_age=3600)
response.set_cookie('user_name', 'xiaohei', max_age=3600)
return response
@app.route('/logout')
def logout():
response = make_response('success')
response.delete_cookie('user_id')
response.delete_cookie('user_name')
return response
if __name__ == '__main__':
app.run(debug=True)
session
- 对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息
- 在服务器端进行状态保持的方案就是
Session
- Session依赖于Cookie
session 流程
- 发送登录请求,发送请求报文,带上要登录的账号和密码
- 服务器处理请求:校验密码,保存用户信息到服务器,每个用户信息对应一个 sid
- 服务器给出相应,把 sid 返回给浏览器(cookie)
- 浏览器把 sid 存储起来
- 下次请求时默认会带上 sid, 服务器可以通过请求取到 sid,在通过 sid 取到对应的用户信息,取到信息就可以知道是否登录,是谁登录
from flask import Flask
from flask import session
app = Flask(__name__)
# 使用 session 的话,需要配置 secret_key
app.config['SECRET_KEY'] = 'asdfghj'
@app.route('/')
def index():
user_id = session.get('user_id', '')
user_name = session.get('user_name', '')
return '%s -- %s' % (user_id, user_name)
@app.route('/login')
def login():
# 假装校验成功
session['user_id'] = "1"
session['user_name'] = 'xiaohei'
return 'success'
@app.route('/logout')
def logout():
session.pop('user_id')
session.pop('user_name')
return 'success'
if __name__ == '__main__':
app.run(debug=True)
上下文
上下文:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。
pycharm 中移动整行代码的快捷键: shift + alt + 上下键
请求上下文
# 请求上下文中的变量 from flask import session from flask import request
request: 封装了 HTTP 请求的内容,针对的是 http 请求
session:用来记录请求会话中的信息,针对的是用户信息。
应用上下文
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。
# 应用上下文中的变量 from flask import current_app from flask import g
current_app: 应用程序上下文,用于存储应用程序中的变量。
g 变量:作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别
注意:不同的请求,会有不同的全局变量
from flask import Flask
# 应用上下文中的变量
from flask import current_app
from flask import g
# 请求上下文中的变量
from flask import session
from flask import request
app = Flask(__name__)
# print(request.method)
# print(session.get['user_id', ''])
# print(current_app.config.get('DEBUG'))
@app.route('/')
def index():
# print(request.method)
print(current_app.config.get('DEBUG'))
return 'index'
if __name__ == '__main__':
app.run(debug=True)
Flask-Script 扩展
通过使用Flask-Script扩展,我们可以在Flask服务器启动的时候,通过命令行的方式传入参数。而不仅仅通过app.run()方法中传参,
pip install flask-script
from flask import Flask
from flask_script import Manager
app = Flask(__name__)
# 创建 manager 与 app 进行关联
manager = Manager(app)
# 可以通过命令行在运行的时候指定运行的端口
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
# 需要使用 manager 去运行
manager.run()
模板
在前面的示例中,视图函数的主要作用是生成请求的响应,这是最简单的请求。实际上,视图函数有两个作用:处理业务逻辑和返回响应内容。在大型应用中,把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本。本节学到的模板,它的作用即是承担视图函数的另一个作用,即返回响应内容。
- 模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
- 使用真实值替换变量,再返回最终得到的字符串,这个过程称为“渲染”
- Flask是使用 Jinja2 这个模板引擎来渲染模板
使用模板的好处:
- 视图函数只负责业务逻辑和数据处理(业务逻辑方面)
- 而模板则取到视图函数的数据结果进行展示(视图展示方面)
- 代码结构清晰,耦合度低
jinja2
- Jinja2:是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板语言。
- 模板语言:是一种被设计来自动生成文档的简单文本格式,在模板语言中,一般都会把一些变量传给模板,替换模板的特定位置上预先定义好的占位变量名。
- Flask提供的 render_template 函数封装了该模板引擎
- render_template 函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值。
使用
- {{}} 来表示变量名,这种 {{}} 语法叫做变量代码块
- 用 {%%} 定义的控制代码块,可以实现一些语言层次的功能,比如循环或者if语句
- 使用 {# #} 进行注释,注释的内容不会在html中被渲染出来
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/demo1')
def demo1():
my_int = 10
my_str = "<h1>哈哈哈</h1>"
my_list = [1, 24, 5, 62, 6]
my_dict = {
"id": "1",
"name": "xiaohei"
}
return render_template('demo6_template.html',
my_int=my_int,
my_str=my_str,
my_list=my_list,
my_dict=my_dict,
)
if __name__ == '__main__':
app.run(debug=True)