页首Html代码

Flask项目创建/http的会话控制/Cookie/Session/请求钩子/异常捕获/context

1. 创建一个flask框架的启动文件

名字可以是app.py/run.py/main.py/index.py/manage.py/start.py
manage.py,代码:

from flask import Flask
app = Flask(__name__)

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

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

代码分析:

# 导入Flask类
from flask import Flask

"""
import_name      Flask程序所在的包(模块),传 __name__ 就可以
                 其可以决定 Flask 在访问静态文件时查找的路径
static_path      静态文件访问路径(不推荐使用,使用 static_url_path 代替)
static_url_path  静态文件访问路径,可以不传,默认为:/ + static_folder
static_folder    静态文件存储的文件夹,可以不传,默认为 static
template_folder  模板文件存储的文件夹,可以不传,默认为 templates
"""
app = Flask(import_name=__name__)


# 编写路由视图
# flask的路由是通过给视图添加装饰器的方式进行编写的。当然也可以分离到另一个文件中。
# flask的视图函数,flask中默认允许通过return返回html格式数据给客户端。
@app.route('/')
def index():
    return "<h1>hello world</h1>"

# 加载项目配置
class Config(object):
    # 开启调试模式
    DEBUG = True

# flask中支持多种配置方式,通过app.config来进行加载,我们会这里常用的是配置类
app.config.from_object( Config )


# 指定服务器IP和端口
if __name__ == '__main__':
    # 运行flask
    app.run(host="0.0.0.0", port=5000)

2. 路由的基本定义

路由和视图的名称必须全局唯一,不能重复,否则报错

# 指定访问路径为 demo1
@app.route('/demo1')
def demo1():
    return 'demo1'

2.1 什么是路由?

一种映射关系.
绑定应用程序和url地址的一种一对一的映射关系
开发项目过程中,所使用的路由往往指代了 框架/项目 中用于完成路由功能的类,一般成为路由类.简称路由.

2.2 路由的两种传递方式

  路由参数是url路劲的一部分

任意路由参数接收

# 路由传递参数[没有限定类型]
@app.route('/user/<user_id>')
def user_info(user_id):
    return 'hello %s' % user_id

限定路由参数接收
  限定路由参数的类型,flask系统自带转换器编写在werkzeug.routing.py文件中.
  底部可以看懂以下字典:

DEFAULT_CONVERTERS = {
   "default": UnicodeConverter,
   "string": UnicodeConverter,
   "any": AnyConverter,
   "path": PathConverter,
   "int": IntegerConverter,
   "float": FloatConverter,
   "uuid": UUIDConverter,
}

  系统自带的转换器具体使用的方式在美中转换器的注释代码中有些,请留意每种转换器出事化的参数.

转换器名称 描述
string 默认类型,接受不带斜杆的任何文本
int 接受正整数
float 接受正浮点值
path 接收string但也接收斜线
uuid 接收UUID(通用唯一识别码) 字符串 xxxx-xxxx-xxxxx-xxxx

代码:

# 限定类型传递路由参数
# flask内置的所有路由转换器是由werkzeug.routing的DEFAULT_CONVERTERS字典进行配置的。
# flask的所有路由转换器,本质上就是路由经过正则来进行匹配获取参数值的。所有的路由转换器都必须直接或间接继承于BaseConverter路由转换器基类
@app.route("/sms/<int(min=10,max=100):mobile>")
def sms(mobile):
    return f"mobile={mobile}"

2.3 自定义路由转换器

  也叫正则匹配;路由参数
  在web开发中,可能出现限制影虎访问规则的场景,这就需要用到正则匹配.

步骤:
   + 导入转换器基类: 在flask中,所有的路由的匹配规则都是使用转换器对象进行记录
   + 自定义转换器: 自定义类继承与转换器基类BaseConverter
   + 添加转换器到默认浏览器的转换字典中
   + 使用自定义转换器实现自定义匹配规则

代码实现
  导入转换器基类

from werkzeug.routing import BaseConverter

  自定义转换器

# 自定义正则转换器
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
    def __init__(self,map,*args):
        super().__init__(map)
        # 正则参数
        self.regex = args[0]

  添加转换器到默认的转换器字典中,并指定转换器使用时名字为: re

# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re
app.url_map.converters['re'] = RegexConverter

  使用转换器去实现自定义匹配规则
    当前此处定义的规则是:手机号码

# 自定义路由转换器
from werkzeug.routing import BaseConverter

class RegexConverter(BaseConverter):
    def __init__(self,map,*args):
        super().__init__(map)
        # 正则参数
        self.regex = args[0]

# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re
app.url_map.converters['re'] = RegexConverter
# 正则匹配路由
@app.route("/sms/<re('1[3-9]\d{9}'):mobile>")
def sms(mobile):
    return f"mobile={mobile}"

  运行测试:http://127.0.0.1:5000/login/1311111111 ,如果访问的url不符合规则,会提示找不到页面

2.4 路由限定请求方式

from flask import Flask,request
# 限制客户端的http请求方法,注意这里与django不一样,flask并没有默认没有内置csrf攻击防范
@app.route(rule="/user", methods=["post","put","get","delete","patch"])
def user():
    # 例如:地址栏中通过  http://127.0.0.1:5000/user?user=1 返回本视图
    print(request.method) # 获取本次客户端的http请求方法         GET
    print(request.query_string)  # 获取本次客户端的查询字符串    b'user=1'
    print(request.path)  # 获取本次客户端请求的路由路径部分[去掉域名端口]    /user
    print(request.url) # 获取本次客户端请求的http url地址        http://127.0.0.1:5000/user?user=1
    # 直接从请求中取到请求方式并返回
    return request.method

3. http的请求和响应

3.1 请求

文档: http://docs.jinkan.org/docs/flask/api.html#flask.request
  request: flask中代表当前的请求的request对象
  作用: 在视图函数中取出本次请求数据
  导入: from flask import request
  代码位置:
     代理类: from flask.app import Request
     源码类: from werkzeug.wrappers.Request
     父类: from werkzeug.wrappers.base_request

3.1.1 常用的父类:

属性 说明 类型
data 记录请求体的数据,并转为字符串
只要是通过其他属性无法识别转换的请求体数据
最终都是保留到data属性中
bytes类型
form 记录请求中的html表单数据 MUltiDict
args 记录请求中的查询的字符串,query_string MultiDict
cookies 记录请求中的cookie信息 Dict
headers 记录请求中的请求头 EnvironHeaders
method 记录请求使用的http方法 GET/POST
url 记录请求的url地址 string
files 记录请求上传的文件列表 *
json 记录ajax请求的json数据 json

3.1.2 获取请求中的各项数据

from flask import Flask,request

# 初始化
app = Flask(import_name=__name__)
# 编写路由视图
@app.route(rule='/')
def index():
    return "<h1>hello world!</h1>"

"""== 获取查询字符串 =="""
@app.route(rule="/args",methods=["post","get"])
def args():
    print(request.args) # 获取查询字符串
    """
    请求地址:
        http://127.0.0.1:5000/args?name=xiaoming&password=123&lve=swimming&lve=shopping
    打印效果:
        ImmutableMultiDict([('name', 'xiaoming'), ('password', '123')])
        ImmutableMultiDict是一个由flask封装的字典类,在字典的基础上,提供了一些其他的方法而已。
        格式:
            ImmutableMultiDict([('键', '值'), ('键', '值')])
        字典本身不支持同名键的,ImmutableMultiDict类解决了键同名问题
        操作ImmutableMultiDict,完全可以操作字典操作,同时还提供了get,getlist方法,获取指定键的1个值或多个值    
    """
    print(request.args["name"]) # xiaoming
    print(request.args.get("name")) # xiaoming
    print(request.args.getlist("lve")) # ['swimming', 'shopping']

    # 把ImmutableMultiDict转换成普通字典
    print(request.args.to_dict(flat=False)) # {'name': ['xiaoming'], 'password': ['123'], 'lve': ['swimming', 'shopping']}
    print(request.args.to_dict(flat=True)) # {'name': 'xiaoming', 'password': '123', 'lve': 'swimming'}

    return "ok"

"""== 获取请求体数据 =="""
@app.route(rule="/data",methods=["post","put","patch"])
def data():
    """接受客户端发送过来的请求体数据,是request.json,request.form,request.files等无法接受的数据,全部会保留到这里"""
    print(request.data) # 

    # 接受表单提交的数据
    print(request.form) # ImmutableMultiDict([('username', 'root'), ('password', '123456')])

    # 接受ajax或其他客户端提交过来的json数据
    print(request.json) # {'username': 'root', 'password': '123456'}

    # 接受上传文件
    avatar = request.files["avatar"] # ImmutableMultiDict([('avatar', <FileStorage: '123.jpg' ('image/jpeg')>)])
    print(avatar) # <FileStorage: '123.jpg' ('image/jpeg')>


    # 获取请求头信息
    print( request.headers ) # 获取全部的而请求头信息
    print( request.headers.get("Host") )
    # 获取自定义请求头
    print( request.headers.get("company") ) # oldboy
    print( request.headers["company"] )     # oldboy
    
    # 本次请求的url地址
    print( request.url) # http://127.0.0.1:5000/data
    print( request.path ) # /data
    
    return "ok"

# 声明和加载配置
class Config():
    DEBUG = True
app.config.from_object(Config)

if __name__ == '__main__':
    # 运行flask
    app.run(host="0.0.0.0")

3.2 响应

flask支持两种响应方式冲顶
      数据响应: 默认响应html文本,也可返回json格式, 或其他格式
      页面响应: 重定向
            url_for 视图之间跳转
  响应的时候, flask也支持自定义http响应状态码

3.2.1 响应html文本

from flask import make_response

@app.route("/")
def index():
   # [默认支持]响应html文本
   return "<img src='http://flask.pocoo.org/static/logo.png'>"
   return make_response("<h1>hello user</h1>") # 等同于上面的一段

3.2.2 返回json数据

在 Flask 中可以直接使用 jsonify 生成一个 JSON 的响应

from flask import Flask, request, jsonify
# jsonify 就是json里面的jsonify

@app.route("/")
def index():
    # 也可以响应json格式代码
    # data = {
    #     "id":1,
    #     "uname":"xiaoming"
    # }
    data = [
        {"id":1,"username":"liulaoshi","age":18},
        {"id":2,"username":"liulaoshi","age":17},
        {"id":3,"username":"liulaoshi","age":16},
        {"id":4,"username":"liulaoshi","age":15},
    ]
    return jsonify(data)

   flask中返回json 数据,都是flask的jsonify方法返回就可以了.

3.2.3 响应其他数据格式

from flask import Flask,make_response,jsonify

app = Flask(__name__)

@app.route("/")
def index():
    """响应处理"""
    # 返回图片等其他自定义格式数据
    # with open("hlw.jpg", "rb") as f:
    #     content = f.read()
    # response = make_response(content)
    # response.headers["Content-Type"] = "image/jpg" # MIME type 类型
    # return response

    with open("myCode.zip","rb") as f:
        content = f.read()

    response = make_response(content)
    response.headers["Content-Type"] = "application/zip"
    return response

if __name__ == '__main__':
    app.run(host="0.0.0.0",port=5000,debug=True)

3.2.4 重定向

重定向到百度页面

from flask import redirect
# 页面跳转响应
@app.route("/user")
def user():
    # 页面跳转 redirect函数就是response对象的页面跳转的封装
    return redirect("http://www.baidu.com")
    # # redirect内部完成的事情如下:
    # from werkzeug.wrappers import Response
    # response = Response()
    # response.headers["Location"] = "http://www.baidu.com"
    # return response

3.2.5 重定向到自己写的视图函数

可以直接填写自己 url 路径

也可以使用 url_for 生成指定视图函数所对应的 url,通过redirect来完成跳转

from flask import url_for

from flask import Flask,redirect, url_for

app = Flask(__name__)

@app.route("/")
def index():
    """响应处理"""
    # 根据视图名称进行页面跳转
    # url_for 根据视图函数名获取对应的uri地址
    url = url_for("user")
    print(url) # 仅仅是根据参数从app.url_map中提取对应的url地址
    return redirect(url)

@app.route("/user")
def user():
    return "用户中心"

if __name__ == '__main__':
    app.run(host="0.0.0.0",port=5000,debug=True)

3.2.6 重定向到带有参数的视图函数

在 url_for 函数中传入路由参数

from flask import Flask,redirect, url_for

app = Flask(__name__)

@app.route("/")
def index():
    """响应处理"""
    # 根据视图名称进行页面跳转
    url = url_for("user",user_id=3)
    print(url) # 仅仅是根据参数从app.url_map中提取对应的url地址
    return redirect(url)

@app.route("/user/<user_id>")
def user(user_id):
    return f"用户中心,user_id={user_id}"

if __name__ == '__main__':
    app.run(host="0.0.0.0",port=5000,debug=True)

3.3 自定义状态码和响应头

在 Flask 中,可以很方便的返回自定义状态码,以实现不符合 http 协议的状态码,例如:status code: 666

@app.route('/demo4')
def demo4():
    return '状态码为 666', 400
  
"""还可以使用make_response创建Response对象,然后通过response对象返回数据"""
from flask import make_response
@app.route("/rep")
def index7():
    response = make_response("ok")
    print(response)
    response.headers["Company"] = "oldboy" # 自定义响应头
    response.status_code = 201 # 自定义响应状态码
    return response

4. http的会话控制

所谓的会话,就是客户端浏览器和服务端网站之间一次完整的交互过程.

会话的开始是在用户通过浏览器第一次访问服务端网站开始.

会话的结束时在用户通过关闭浏览器以后,与服务端断开.

所谓的会话控制,就是在客户端浏览器和服务端网站之间,进行多次http请求响应之间,记录、跟踪和识别用户的信息而已。

因为 http 是一种无状态协议,浏览器请求服务器是无状态的
无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。

无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且服务器也会在处理页面完毕之后销毁页面对象。

有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等

实现状态保持主要有两种方式:

  • 在客户端存储信息使用Cookie,token[jwt,oauth]
  • 在服务器端存储信息使用Session
补充:
token是一种用户身份认证信息的技术,一般我们称之为:Token认证。
翻译中文:token一般叫令牌
本质就是一个经过特殊处理的字符串,往往在字符串内部隐藏着识别用户身份信息的关键内容。
一般开发中,token往往都是以识别用户身份为目的来使用的。

一般使用情况下,token会以用户身份信息,当前事件戳,随机数等因子构成的。
当然,更多情况下,token一般分三段:"头部.载荷.签证"
像实际开发中,我们一般说的jwt,csrf等等场景里面的token都是这一类的。

Cookie是由服务器端生成,发送给客户端浏览器,浏览器会自动将Cookie的key/value保存,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie的key/value可以由服务器端自己定义。

使用场景: 登录状态, 浏览历史, 网站足迹, 购物车 [不登录也可以使用购物车]

Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用

Cookie基于域名安全,不同域名的Cookie是不能互相访问的

如访问oldboyedu.com时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到oldboyedu写的Cookie信息

浏览器的同源策略针对cookie也有限制作用.

当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息

4.1.1 cookie操作

设置cookie需要通过flask的Response响应对象来进行设置,由响应对象会提供了方法set_cookie给我们可以快速设置cookie信息。

from flask import Flask,make_response,request

app = Flask(__name__)

@app.route('/set_cookie')
def set_cookie():
    response = make_response('set cookie')
    response.set_cookie('age', '19') # 过期时间是会话期,关闭浏览器以后,自动删除
    response.set_cookie('username', 'xiaoming', max_age=3600) # 在指定max_age时间以后,才会自动删除
    return response

@app.route('/get_cookie')
def get_cookie():
    # 获取客户端发送过来的cookie要通过request.cookies来完成
    # request.cookies 获取全部cookie
    username = request.cookies.get('username')
    age = request.cookies.get('age')
    return f"username={username}&age={age}"

@app.route("/del_cookie")
def del_cookie():
    # 删除cookie,cookie的删除必须要通过response对当前cookie设置无效的值或者过期的有效时间,让浏览器自动删除
    response = make_response("del cookie")
    response.set_cookie("username","",expires=0)
    return response

if __name__ == '__main__':
    app.run(host="0.0.0.0",port=5000,debug=True)

4.2 Session

  对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息

  在服务器端进行状态保持的方案就是`Session`

  **Session依赖于Cookie**,session的ID一般默认通过cookie来保存到客户端。

  flask中的session需要加密,所以使用session之前必须配置SECRET_KEY选项,否则报错.

  session的有效期默认是会话期,会话结束了,session就废弃了。

如果将来希望session的生命周期延长,可以通过修改cookie中的sessionID来完成配置。


###4.2.1 设置session
```python
from flask import session
@app.route('/set_session')
def set_session():
  session['username'] = 'xiaoming'
  return 'ok!'

4.2.2 获取session

from flask import session
@app.route('/get_session')
def get_session():
    return session.get('username')

4.2.3 session操作

from flask import Flask,session

app = Flask(__name__)

class Config(object):
    # 使用session之前,必须先设置秘钥
    SECRET_KEY = "123903tklasdlkas@"
app.config.from_object(Config)

@app.route('/set_session')
def set_session():
    """设置session数据"""
    session["uname"] = "xiaoming"
    session["age"] = 13
    session["book_list"] = [{"title":"book_name"},{"title":"book_name"}]
    return "set_session"

@app.route('/get_session')
def get_session():
    """获取session数据"""
    print(session.get("uname"))
    print(session.get("age"))
    print(session.get("book_list"))
    return "get_session"

@app.route('/del_session')
def del_session():
    """删除session数据"""
    if session.get("uname"):
        del session["uname"]
    return "ok"


if __name__ == '__main__':
    app.run(host="0.0.0.0",port=5000,debug=True)

4.3 请求钩子hook

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

  • 在项目运行开始时,建立数据库连接;
  • 在客户端请求开始时,根据需求进行权限校验;
  • 在请求结束视图返回数据时,指定数据的交互格式;

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

请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子(注意:名字是固定):

  • before_first_request
    • 在处理第一个请求前执行[项目初始化时的钩子]
  • before_request
    • 在每一次请求前执行
    • 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
  • after_request
    • 如果没有抛出错误,在每次请求后执行
    • 接受一个参数:视图函数作出的响应
    • 在此函数中可以对响应值在返回之前做最后一步修改处理
    • 需要将参数中的响应在此参数中进行返回
  • teardown_request:
    • 在每一次请求后执行
    • 接受一个参数:错误信息,如果有相关错误抛出
    • 需要设置flask的配置DEBUG=False,teardown_request才会接受到异常对象。

代码

from flask import Flask,request

# 初始化
app = Flask(import_name=__name__)

# 声明和加载配置
class Config():
    DEBUG = True
app.config.from_object(Config)

@app.before_first_request
def before_first_request():
    """
    这个钩子会在项目启动后第一次被用户访问时执行
    可以编写一些初始化项目的代码,例如,数据库初始化,加载一些可以延后引入的全局配置
    """
    print("----before_first_request----")
    print("系统初始化的时候,执行这个钩子方法")
    print("会在接收到第一个客户端请求时,执行这里的代码")

@app.before_request
def before_request():
    """
    这个钩子会在每次客户端访问视图的时候执行
    # 可以在请求之前进行用户的身份识别,以及对于本次访问的用户权限等进行判断。..
    """
    print("----before_request----")
    print("每一次接收到客户端请求时,执行这个钩子方法")
    print("一般可以用来判断权限,或者转换路由参数或者预处理客户端请求的数据")

@app.after_request
def after_request(response):
    print("----after_request----")
    print("在处理请求以后,执行这个钩子方法")
    print("一般可以用于记录会员/管理员的操作历史,浏览历史,清理收尾的工作")
    response.headers["Content-Type"] = "application/json"
    response.headers["Company"] = "python oldboy..."
    # 必须返回response参数
    return response


@app.teardown_request
def teardown_request(exc):
    print("----teardown_request----")
    print("在每一次请求以后,执行这个钩子方法")
    print("如果有异常错误,则会传递错误异常对象到当前方法的参数中")
    # 在项目关闭了DEBUG模式以后,则异常信息就会被传递到exc中,我们可以记录异常信息到日志文件中
    print(exc)

# 编写路由视图
@app.route(rule='/')
def index():
    print("-----------视图函数执行了---------------")
    return "<h1>hello world!</h1>"

if __name__ == '__main__':
    # 运行flask
    app.run(host="0.0.0.0")
  • 在第1次请求时的打印:
----before_first_request----
系统初始化的时候,执行这个钩子方法
会在接收到第一个客户端请求时,执行这里的代码
----before_request----
127.0.0.1 - - [04/Aug/2020 14:40:22] "GET / HTTP/1.1" 200 -
每一次接收到客户端请求时,执行这个钩子方法
一般可以用来判断权限,或者转换路由参数或者预处理客户端请求的数据
-----------视图函数执行了---------------
----after_request----
在处理请求以后,执行这个钩子方法
一般可以用于记录会员/管理员的操作历史,浏览历史,清理收尾的工作
----teardown_request----
在每一次请求以后,执行这个钩子方法
如果有异常错误,则会传递错误异常对象到当前方法的参数中
None
  • 在第2次请求时的打印:
----before_request----
每一次接收到客户端请求时,执行这个钩子方法
一般可以用来判断权限,或者转换路由参数或者预处理客户端请求的数据
127.0.0.1 - - [04/Aug/2020 14:40:49] "GET / HTTP/1.1" 200 -
-----------视图函数执行了---------------
----after_request----
在处理请求以后,执行这个钩子方法
一般可以用于记录会员/管理员的操作历史,浏览历史,清理收尾的工作
----teardown_request----
在每一次请求以后,执行这个钩子方法
如果有异常错误,则会传递错误异常对象到当前方法的参数中
None

5. 异常捕获

5.1 主动抛出HTTP异常

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

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

abort在工作中基本不会被使用,工作中的异常抛出往往在业务错误的时候使用raise进行抛出错误类型,而不是抛出http异常。

abort一般用于权限等页面上错误的展示提示.

5.2 捕获错误

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

代码:

from flask import Flask,request,abort

app = Flask(__name__)

# errorhandler 可以捕获指定http状态码的异常
@app.errorhandler(400)
def internal_server_error(e):
    print(e)
    return '对不起,参数有误!'

# errorhandler 也可以捕获指定的内置异常或者自定义的异常
class APIError(Exception):
    pass

# 捕获自定义异常
@app.errorhandler(APIError)
def api_error(e):
    print(e)
    return "api请求有误"

# 捕获标准异常[python内置声明的]
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
    print(e)
    return '除数不能为0'

@app.route("/")
def index():
    # # 主动抛出HTTP异常
    # if not request.args.get("uname"):
    #     abort(400)

    # 抛出自定义异常并进行处理
    # raise APIError("api接口发生异常!!!!")

    1/0

    return "ok"


if __name__ == '__main__':
    app.run(host="0.0.0.0",port=5000,debug=True)

6. context

执行上下文:即语境,语意,在程序中可以理解为在代码执行到某一行时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情。

Flask中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息[变量、函数、类与对象等信息]。

Flask中有两种上下文,请求上下文(request context)和应用上下文(application context)。

  1. application 指的就是当你调用app = Flask(__name__)创建的这个对象app
  2. request 指的是每次http请求发生时,WSGI server(比如gunicorn/uwsgi)调用Flask.__call__()之后,在Flask对象内部创建的Request对象;
  3. application 表示用于响应WSGI请求的应用本身,request 表示每次http请求;
  4. application的生命周期大于request,一个application存活期间,可能发生多次http请求,所以,也就会有多个request

6.1 请求上下文(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')获取用户信息。

请求上下文提供的变量/属性/方法/函数/类与对象,只能在视图中或者被视图调用的地方使用

6.2 应用上下文(application context)它的字面意思是

应用上下文,但它不是一直存在的,它只是request context 中操作当前falsk应用对象 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的flask应用相关的信息,它是伴 request 而生,随 request 而灭的。

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

6.2.1 current_app

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

- 应用的启动脚本是哪个文件,启动时指定了哪些参数
- 加载了哪些配置文件,导入了哪些配置
- 连接了哪个数据库
- 有哪些可以调用的工具类、常量
- 当前flask应用在哪个机器上,哪个IP上运行,内存多大

```python
from flask import Flask,request,session,current_app,g

# 初始化
app = Flask(import_name=__name__)

# 声明和加载配置
class Config():
    DEBUG = True
app.config.from_object(Config)

# 编写路由视图
@app.route(rule='/')
def index():
    # 应用上下文提供给我们使用的变量,也是只能在视图或者被视图调用的地方进行使用,
    # 但是应用上下文的所有数据来源于于app,每个视图中的应用上下文基本一样
    print(current_app.config)   # 获取当前项目的所有配置信息
    print(current_app.url_map)  # 获取当前项目的所有路由信息

    return "<h1>hello world!</h1>"

if __name__ == '__main__':
    # 运行flask
    app.run(host="0.0.0.0")

6.2.3 g变量

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

g.name='abc' # name是举例,实际要保存什么数据到g变量中,可以根据业务而定,你可以任意的数据进去

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

from flask import Flask,request,session,current_app,g

# 初始化
app = Flask(import_name=__name__)

# 声明和加载配置
class Config():
    DEBUG = True
app.config.from_object(Config)

@app.before_request
def before_request():
    g.name = "root"

def get_two_func():
    name = g.name
    print("g.name=%s" % name)

def get_one_func():
    get_two_func()

# 编写路由视图
@app.route(rule='/')
def index():
    # 请求上下文提供的变量/属性/方法/函数/类与对象,只能在视图中或者被视图调用的地方使用
    # 请求上下文里面信息来源于每次客户端的请求,所以每个视图中请求上下文的信息都不一样
    # print(session)

    # 应用上下文提供给我们使用的变量,也是只能在视图或者被视图调用的地方进行使用,
    # 但是应用上下文的所有数据来源于于app,每个视图中的应用上下文基本一样
    print(current_app.config)   # 获取当前项目的所有配置信息
    print(current_app.url_map)  # 获取当前项目的所有路由信息
    get_one_func()
    return "<h1>hello world!</h1>"


if __name__ == '__main__':
    # 运行flask
    app.run(host="0.0.0.0")

6.3 两者区别

  • 请求上下文:保存了客户端和服务器交互的数据,一般来自于客户端。
  • 应用上下文:flask 应用程序运行过程中,保存的一些配置信息,比如路由列表,程序名、数据库连接、应用信息等
posted @ 2021-05-20 21:11  哈士骑KT猫  阅读(83)  评论(0编辑  收藏  举报