03.Flask之路由系统及视图函数(二)

异常捕获

HTTP 异常主动抛出

  • abort 方法
    • 抛出一个给定状态代码的 HTTPException 或者 指定响应,例如想要用一个页面未找到异常来终止请求,你可以调用 abort(404)。
  • 参数:
    • code – HTTP的错误状态码
@app.route("/demo1")
def demo1():
    abort(404)      # 抛出404错误
    return "demo1"

抛出状态码的话,只能抛出 HTTP 协议的错误状态码

捕获错误

  • errorhandler 装饰器
    • 注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
  • 参数:
    • code_or_exception – HTTP的错误状态码或指定异常
  • 例如统一处理状态码为500的错误给用户友好的提示:
# 捕获相关状态码,然后进行处理
@app.errorhandler(404)
def internal_server_error(e):
    return '服务器搬家了'


# 捕获指定异常,然后进行处理 @app.errorhandler(ZeroDivisionError) def zero_division_error(e): return '除数不能为0'

请求勾子

在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:

  • 在请求开始时,建立数据库连接;
  • 在请求开始时,根据需求进行权限校验;
  • 在请求结束时,指定数据的交互格式;

为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。

请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:

  • before_first_request
    • 在处理第一个请求前执行
  • before_request
    • 在每次请求前执行
    • 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
  • after_request
    • 如果没有抛出错误,在每次请求后执行
    • 接受一个参数:视图函数作出的响应
    • 在此函数中可以对响应值在返回之前做最后一步修改处理
    • 需要将参数中的响应在此参数中进行返回
  • teardown_request:
    • 在每次请求后执行
    • 接受一个参数:错误信息,如果有相关错误抛出

 

from flask import Flask
from flask import abort

app = Flask(__name__)


# 在第一次请求之前调用,可以在此方法内部做一些初始化操作
@app.before_first_request
def before_first_request():
    print("before_first_request")


# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数
@app.before_request
def before_request():
    print("before_request")
    # if 请求不符合条件:
    #     return "laowang"      # 如果直接写return的话,会不执行与路由匹配的视图函数


# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
    print("after_request")
    response.headers["Content-Type"] = "application/json"
    return response


# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(e):
    print("teardown_request")


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

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

装饰器路由具体实现梳理

Flask有两大核心:Werkzeug和Jinja2

- Werkzeug实现路由、调试和Web服务器网关接口
- Jinja2实现了模板。

Werkzeug是一个遵循WSGI协议的python函数库

- 其内部实现了很多Web框架底层的东西,比如request和response对象;
- 与WSGI规范的兼容;支持Unicode;
- 支持基本的会话管理和签名Cookie;
- 集成URL请求路由等。

Werkzeug库的 routing 模块负责实现 URL 解析。不同的 URL 对应不同的视图函数,routing模块会对请求信息的URL进行解析,匹配到URL对应的视图函数,执行该函数以此生成一个响应信息。

routing模块内部有:

  • Rule类
    • 用来构造不同的URL模式的对象,路由URL规则,通过打印app.url_map可以看到每一个路由和视图函数都是一条匹配规则。
  • Map类
    • 存储所有的URL规则和一些配置参数
  • BaseConverter的子类
    • 负责定义匹配规则
  • MapAdapter类
    • 负责协调Rule做具体的匹配的工作

request

request 就是flask中代表当前请求的 request 对象,其中一个请求上下文变量(理解成全局变量,在视图函数中直接使用可以取到当前本次请求),如果获取的是复选框信息,用getlist,类似:request.form.getlist("sel")。

常用的属性如下:

属性说明类型
data 记录请求的数据,并转换为字符串 *
form 记录请求中的表单数据 MultiDict
args 记录请求中的查询参数 MultiDict
cookies 记录请求中的cookie信息 Dict
headers 记录请求中的报文头 EnvironHeaders
method 记录请求使用的HTTP方法 GET/POST
url 记录请求的URL地址 string
files 记录请求上传的文件 *

示例

@app.route('/', methods=['POST'])
def index():
    pic = request.files.get('pic')
    pic.save('./static/aaa.png')
    return 'index'

 

 

 说明:当传值为select多选框的多选形式时,可以用getlist进行相关的数据获取,结果为列表数据。

状态保持

  • 因为 http 是一种无状态协议,浏览器请求服务器是无状态的。
  • 无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。
  • 无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且服务器也会在处理页面完毕之后销毁页面对象。
  • 有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
  • 实现状态保持主要有两种方式:
    • 在客户端存储信息使用Cookie
    • 在服务器端存储信息使用Session

无状态协议:

  1. 协议对于事务处理没有记忆能力
  2. 对同一个 url 请求没有上下文关系
  3. 每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况
  4. 服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器
  5. 人生若只如初见
  • Cookie:指某些网站为了辨别用户身份、进行会话跟踪而储存在用户本地的数据(通常经过加密)。
    • 复数形式Cookies。
    • Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明。
    • Cookie是由服务器端生成,发送给客户端浏览器,浏览器会将Cookie的key/value保存,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。
    • Cookie的key/value可以由服务器端自己定义。
  • 应用:
    • 最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续,这些都是Cookie的功用。
    • 网站的广告推送,经常遇到访问某个网站时,会弹出小窗口,展示我们曾经在购物网站上看过的商品信息。
    • 购物车,用户可能会在一段时间内在同一家网站的不同页面中选择不同的商品,这些信息都会写入Cookie,以便在最后付款时提取信息。
  • 提示:

    • Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用
    • Cookie基于域名安全,不同域名的Cookie是不能互相访问的
      • 如访问itcast.cn时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到itcast.cn写的Cookie信息
      • 浏览器的同源策略
    • 当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息

设置Cookie

from flask import Flask, request, make_response
@app.route("/login")
def login():
    response = make_response("success")
    response.set_cookie("username", "laowang")
    return response

 设置过期时间

from flask import Flask, request, make_response
@app.route("/login")
def login():
    response = make_response("success")
    response.set_cookie("username", "laowang", max_age=3600)
    return response

获取cookie

@app.route('/')
def index():
    resp = request.cookies.get('username')
    print(resp)
    return 'index'

删除cookie

@app.route("/logout")
def logout():
    response = make_response("del success!")
    response.delete_cookie("username")
    return response

Session

  • 对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息
  • 在服务器端进行状态保持的方案就是Session
  • Session依赖于Cookie

设置session

from flask import Flask, request, make_response, session
@app.route("/login")
def login():
    session['username'] = 'laowang'
    return "success"

当设置完成,运行之后,会报下面的错误:

 

这是我们并没有设置密钥,加密钥会保证我们后台的数据安全。所以接下来,需要设置secret_key。

app.config["SECRET_KEY"] = "dkfjaljflj"

获取session

    username = session["username"]      # 这种写法没有取到会报错
    username = session.get('username', "")      # 推荐,没有对应session是会赋值为空字符串

删除session

@app.route("/logout")
def logout():
    session.pop("username")     # 删除没有的session会报错
    session.pop("username", None)     # 删除没有的session不会报错
    return "success"

上下文

上下文:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。

Flask中有两种上下文,请求上下文和应用上下文

请求上下文(request context)

思考:在视图函数中,如何取到当前请求的相关数据?比如:请求地址,请求方式,cookie等等

在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、session

  • request
    • 封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。
  • session
    • 用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。

 

浏览器的每一次请求都会被封装为一个请求对象,而每一次请求封装的对象都是不一样的。

示例:

from flask import Flask, request

app = Flask(__name__)

rq = request.method

@app.route('/')
def hello_world():
    print(request.method)
    return 'Hello World!'

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

当运行之后,会报错:

错误的原因是在请求上下文之外工作,从这里我们也可以分析出请求上下文的范围。

应用上下文(application context)

它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。

应用上下文对象有:current_app,g

current_app

应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:

  • 应用的启动脚本是哪个文件,启动时指定了哪些参数
  • 加载了哪些配置文件,导入了哪些配置
  • 连了哪个数据库
  • 有哪些public的工具类、常量
  • 应用跑再哪个机器上,IP多少,内存多大

示例:

from flask import Flask, current_app

app = Flask(__name__)

print(current_app.config.get("DEBUG"))

@app.route('/')
def hello_world():
    print(current_app.config.get("DEBUG"))
    return 'Hello World!'

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

运行之后,会显示下面错误:

 

 这个错误表示我们在应用程序上下文之外工作。

g变量

g 作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别

g.name='abc'

 

注意:不同的请求,会有不同的全局变量

注意:在项目部分通过具体需求进行讲解

Flask-Script 扩展

通过使用Flask-Script扩展,我们可以在Flask服务器启动的时候,通过命令行的方式传入参数。而不仅仅通过app.run()方法中传参,比如我们可以通过:

python hello.py runserver -host ip地址

以上代码告诉服务器在哪个网络接口监听来自客户端的连接。默认情况下,服务器只监听来自服务器所在的计算机发起的连接,即localhost连接。

我们可以通过python hello.py runserver --help来查看参数。

代码实现

  • 安装 Flask-Script 扩展
pip install flask-script -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 集成 Flask-Script
from flask import Flask
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)


@app.route("/")
def index():
    return "Hello World!"

if __name__ == '__main__':
    manager.run()
修改代码

 

经过修改,使用右击空白处运行的方式就不能运行项目了,需要使用命令行:

python test.py runserver

下面是运行结果:

 这是使用默认的启动方式,我们还可以自定制启动方式,首先,我们使用下面命令查看帮助文档:

python test.py runserver --help

 比如下面方式就是将项目运行在8080端口,并打开debug模式。

python test.py runserver -p 8080 -d

注意:在项目部署、数据迁移、创建管理员等等内容时,都需要用到这个插件。

到现在有一个问题,就是每次启动项目我们就需要使用命令,而且使用命令之后,就没有办法点击报错信息,从而快速的找到错误代码。为了解决这个问题,我们可以使用PyCharm中的小功能:

第一步:

 第二步:

写入自定制的脚步命令

 第三步:

右击,运行,没毛病!

posted @ 2019-10-27 20:57  苦行僧95  阅读(311)  评论(0)    收藏  举报