1、flask基础

一、环境安装

  • 安装python(详见其他专栏)
  • 创建项目: 01_flask
  • 创建虚拟环境
# 创建虚拟环境
python -m venv .env

# 进入虚拟环境运行
source .env/bin/activate

# 虚拟环境安装包
# pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install flask -i https://pypi.tuna.tsinghua.edu.cn/simple

二、启动第一个项目

  • helloWorld.py
# 导包
from flask import Flask

# 初始化, Flask有四个参数, 分别是
app = Flask(__name__)


# 定义视图,即路由函数
@app.route('/')
def index():
    return 'hello zpp'


# 主项目入口
if __name__ == '__main__':
    app.run(host="0.0.0.0")
  • 参数分别是
    • import_name: 传__name__ 就可以, 找到整个项目的根目录,也就是访问静态文件时候要查找的路径
    • static_url_path: 可以手动更改静态文件路径
    • static_folder : 静态文件存储的文件夹
    • template_folder : 模板文件存储的文件夹

三、集中管理项目的配置信息

3.1 从配置对象中加载

  • app.config.from_object(配置对象)
  • helloworld.py
# 配置对象加载配置信息
class DefaultConfig(object):
    """
    默认配置
    """
    SECRET_KEY = 'fhsdjaslkfa1234'


app = Flask(__name__)

# 加载
app.config.from_object(DefaultConfig)

@app.route('/')
def index():
    # 读取配置信息
    print(app.config['SECRET_KEY'])
    return 'hello zpp'


if __name__ == '__main__':
    app.run(host="0.0.0.0")

3.2 从配置文件中加载

  • helloworld.py
from flask import Flask

app = Flask(__name__)

# 加载
app.config.from_pyfile('settings.py')


@app.route('/')
def index():
    # 读取配置信息
    print(app.config['SECRET_KEY'])
    return 'hello zpp'


if __name__ == '__main__':
    app.run(host="0.0.0.0")

  • settings.py
SECRET_KEY = '从配置文件中读取'

3.3 从环境变量中加载

  • 在linux中设置和读取环境变量
# 设置
export 变量名 = 变量值

# 读取
echo $变量名

# 查看所有
env
  • 设置环境变量: export PROJECT_SETTING = settings.py
  • helloworld.py
# 加载
app.config.from_envvar('PROJECT_SETTING')

# 加载  silent:默认为Flase 没有直接报错, True: 如果没找到返回None
app.config.from_envvar('PROJECT_SETTING', silent=True)

3.4 总结

  • 三种配置文件的优缺点
app.config.from_object(配置对象)
- 优点: 继承--> 复用
- 缺点: 敏感数据暴漏

app.config.from_pyfile(配置文件)
- 优点: 独立文件, 保护敏感数据
- 缺点: 不能继承, 文件路径固定, 不灵活

app.config.from_envvar('环境变量名')
优点: 独立文件 保护敏感数据, 文件路径不固定 灵活
缺点: 不方便, 要记得设置环境变量
  • 实际应用中一般用配置文件+环境变量组合: 默认配置后 用环境变量覆盖
from flask import Flask


# 配置对象加载配置信息
class DefaultConfig(object):
    """
    默认配置
    """
    SECRET_KEY = 'fhsdjaslkfa1234'


# 测试环境: 调式开发模式下
class DevelopmentConfig(DefaultConfig):
    DEBUG = True


# 构建工厂函数
def create_flask_app(config):
    """
    构建flask工厂函数
    :param config:
    :return:
    """
    app = Flask(__name__)
    # 加载
    app.config.from_object(DefaultConfig)
    app.config.from_envvar('PROJECT_SETTING')
    return app


app = create_flask_app(DevelopmentConfig)


@app.route('/')
def index():
    # 读取配置信息
    print(app.config['SECRET_KEY'])
    return 'hello zpp'


if __name__ == '__main__':
    app.run(host="0.0.0.0")
  • 1.0 以后的终端启动
# 可以不要
# if __name__ == '__main__':
#     # 运行flask提供的测试服务器
#     app.run(host="0.0.0.0", port=5001, debug=True, processes=1)

# 运行哪个文件是FLASK_APP这个环境变量确定
export FLASK_APP=Helloworld
flask run -h 地址 -p 端口

# 生产模式与开发模式的控制, 通过FLASK_ENV环境变量指明
export FLASK_ENV=production  # 运行在生产模式, 未指明则默认此方式
export FLASK_ENV=development # 运行在开发模式

四、路由与蓝图

4.1 路由

  • 获取全部路由
# 命令行方式
flask routes

# 代码中
@app.route('/')
def route_map():
    """
    主视图  返回所有视图网址
    :return:
    """
    rules_iterator = app.url_map.iter_rules()
    return json.dumps({rule.endpoint: rule.rule for rule in rules_iterator})
  • 请求方式
GET:
OPTIONS(自带):简化版的GET请求, 用于询问服务器接口信息, 比如接口允许的请求方式, 允许的请求源头域名
HEAD(自带): 简化版本GET请求, 只返回GET请求处理的响应头, 不返回响应体
CORS跨域: 
	- 例如:127.0.0.1:8000  ->想要访问  127.0.0.1:8001
	- optins 127.0.0.1:8000 , 返回response 的allow-origin 允许
	- 再访问127.0.0.1:8001

还有: POST PUT DELETE PATCH
在路由函数中
@app.route('/', methods=['GET', 'POST'])

4.2 蓝图

  • 在flask中, 使用蓝图Blueprint来分模块组织管理, 类似于django中的app
  • 使用方法
# 1、创建一个蓝图
user_bp = Blueprint('user', __name__)

# 2、在蓝图上操作
@user_bp.route('/')
def user_profile():
	return 'user_profile'

# 在应用对象上注册蓝图对象
app.register_blueprint(user_bp)
  • 指定参数
    url_prefix = ‘路由地址’

五、请求与响应

5.1 处理请求

请求报文
GET /users/1?a=1 HTTP/1.1
Content-Type: application/json
...
body -> file from json xml

def func(request, ...):
	request.
<>: 就是转换器
@jump.route('/users/<user_id>')
def user_info(user_id):
    print(user_id)
    print(type(user_id))
    return user_id

# 访问: http://127.0.0.1:5001/users/123
@jump.route('/users/<int:user_id>')
def user_info(user_id):
    print(user_id)
    print(type(user_id))
    return str(user_id)
# 转换器
default: 
string:
any:
path:
int:
float:
uuid:
  • 自定义转换器
# 自定义转换器
from werkzeug.routing import BaseConverter


class MobileConverter(BaseConverter):
    """
    手机号格式
    """
    regex = r'1[3-9]\d{9}'


app.url_map.converters['mobile'] = MobileConverter


@app.route('/phone/<mobile:phone>')
def user_info(phone):
    print(phone)
    print(type(phone))
    return str(phone)
  • request
data:	记录请求的数据, 并转换为字符串
form:	记录请求中的表单数据
args:	记录请求中的查询数据
cookies:	记录请求中的cookie信息
headers:	记录请求中的报文信息
method:		记录请求使用的HTTP方法
url:		记录请求的url地址
files:		记录请求上传的文件
  • 举例
    在这里插入图片描述
@app.route('/upload', methods=['POST'])
def upload_file():
    f = request.files['pic']
    f.save('./demo.png')
    return 'ok'

5.2 处理响应

HTTP/1.1 200 OK
Content-Type: application/json
..
body

六 、重定向

# 重定向
from flask import redirect

@app.route('/demo')
def demo():
    return redirect('https://www.baidu.com/')

七、返回json

  • json.dumps()
  • jsonify()
    • 转换成json字符串,
    • 设置了响应头Content-Type: applications/json
from flask import jsonify


@app.route('/demo2')
def demo2():
    json_dict = {
        'user_id': 10,
        'uesr_name': 'laowang'
    }
    return jsonify(json_dict)

八、Cookie和Session

8.1 Cookie

# 设置Cookie
from flask import Flask, make_response

app = Flask(__name__)


@app.route('/set_cookie')
def set_cookie():
    resp = make_response('set cookie ok')
    # resp.set_cookie('cookie名字', 'cookie值', max_age=有效时间)
    resp.set_cookie('username', 'itcast', max_age=3600)  # 设置有效期
    return resp


# 读取cookie
@app.route('/get_cookie')
def sget_cookie():
    resp = request.cookies.get('username')
    return resp


# 删除cookie
@app.route('/delete_cookie')
def delete_cookie():
    resp = make_response('hello')
    resp.delete_cookie('username')
    return resp

8.2 Session

  • 需要先设置SECRET_KEY : 签名 为了让浏览器更改我Session的时候我知道,
  • cookie和session 是存在缓存中的
class DefaultConfig(object):
	SECRET_KEY = 'qweropfksdmfahf56fs5f4a'
	
app.config.from_object(DefaultConfig)

# 或者直接设置
app.secret_key= 'qweropfksdmfahf56fs5f4a'
  • 代码验证
from flask import Flask
from flask import session

app = Flask(__name__)
app.secret_key = 'qweropfksdmfahf56fs5f4a'


# 设置
@app.route('/set_session')
def set_session():
    session['username'] = 'itcast'
    return 'set session ok'


# 读取
@app.route('/get_session')
def get_session():
    username = session.get('username')
    return f'session is {username}'


if __name__ == '__main__':
    # 运行flask提供的测试服务器
    app.run(host="0.0.0.0", port=5000, debug=True, processes=1)

在这里插入图片描述

  • flask将sessions数据保存到了哪里?
  • 保存到了缓存里

九、异常处理

  • HTTP异常主动抛出
    • abort:
# 读取文章(用abort捕获异常)
@app.route('/articles')
def get_articles():
    channel_id = request.args.get('channel_id')
    if channel_id is None:
        abort(400)  # 400 Bad Request
    return f'you wanna get articles of channel {channel_id}'
  • errorhandler 装饰器
# 异常装饰器
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
    return '除数不能为0'


@app.errorhandler(500)
def zero_division_error(e):
    return '服务器搬家了'

十、请求钩子

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

    • 在请求开始时, 建立数据库连接
    • 在请求开始时, 根据请求进行权限校验
    • 在请求结束时, 指定数据的交互格式
  • 为了让每个视图函数避免编写重复功能的代码, Flask提供了通用设施的功能, 即请求钩子, 请求钩子是通过装饰器的形式实现的, Flask支持如下四种请求钩子:

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

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

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 not hasattr(g, 'user'):
    #     setattr(g, 'user', 'xxxx')
    #     return '不符合条件不执行视图函数'


# 在执行完试图函数的时候调用, 并且把视图函数所生成的响应传入, 可以在此方法中对响应做最后一部统一的处理
@app.after_request
def after_request(response):
    print('after_request')
    response.headers['Content_type'] = 'application/jaon'
    return response


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


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


if __name__ == '__main__':
    # 运行flask提供的测试服务器
    app.run(host="0.0.0.0", port=5000, debug=True, processes=1)

十一、上下文

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

11.1 请求上下文(request context)

思考: 在视图函数中, 如何取到当前请求的相关数据? 比如: 请求地址、请求方式、cookie等等
在falsk中, 可以直接在视图函数中使用request对象进行获取相关数据, 而request就是请求上下文的对象, 保存了当前本次请求的相关数据, 请求上下文对现有:request\session

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

11.2 应用上下文 (application context)

  • current_app
    • 用了蓝图以后, 可能有的够不到app, 所以用current_app
  • 主函数.py
from flask import Flask
from flask import session, request, abort, g
# 注册蓝图
from passport import bp

app = Flask(__name__)

# 模拟redis_cli
app.redis_cli = 'redis client'
app.register_blueprint(bp)
if __name__ == '__main__':
    # 运行flask提供的测试服务器
    app.run(host="0.0.0.0", port=5000, debug=True, processes=1)
  • passport.py
from flask import Blueprint, current_app

bp = Blueprint('passport', __name__)


@bp.route('/bp')
def viewFunc():
    print(current_app.redis_cli)
    return 'ok'
  • g : 相当于全局保存, 就是一个容器 每次请求都会重设这个变量
from flask import Blueprint, current_app, g

bp = Blueprint('passport', __name__)


@bp.route('/bp')
def viewFunc():
    print(current_app.redis_cli)
    return 'ok'


def db_query():
    uid = g.user_id
    uname = g.username
    print(f'user_id: {uid}')


@bp.route('/')
def get_user_profile():
    user_id = 123
    username = 'itcast'

    g.user_id = user_id
    g.username = username
    ret = db_query()

    return 'hello  world'

11.3 上下文原理扩展

上下文实现的原理 -> Threadlocal 线程局部变量
from flask import request

/artcles?channel_id=123 -> request.args.get('channel_id') -> 123 Thread id A
/artcles?channel_id=123 -> request.args.get('channel_id') -> 234 Thread id A

request -> 全局变量 : 虽然是全局变量, 但是args内部逻辑相当于多线程
可以想象成是字典
request.args = {
	'thread_a_id': 123,
	'thread_b_id': 234
}

十二、综合认证设计思路

12.1 需求

  • 构建认证机制
  • 对于特定视图可以提供强制要求用户登录的限制 -----------> 装饰器
  • 对于所有视图, 无论是否强制要求用户登录, 都可以在视图中尝试获取用户认证后的身份信息 ------------> 请求钩子

12.2 实现

  • 思路
    在这里插入图片描述
from flask import Flask, request, abort, current_app, g, make_response

app = Flask(__name__)


# 请求钩子(尝试判断用户的身份, 对于未登录的用户不做处理, 放行) (用g对象保存用户身份信息:g.user_id=123, g.user_id= None)
@app.before_request
def authentication():
    user_id = request.cookies.get('user_id')
    if user_id:
        g.user_id = user_id
    else:
        g.user_id = None


# 强制登陆的装饰器
def login_required(func):
    def wrapper(*args, **kwargs):
        # 判断用户是否登录
        if g.user_id is None:
            abort(401)  # 没有经过认证的意思
        else:
            # 已登录
            return func(*args, **kwargs)

    return wrapper


# 普通视图视图
@app.route('/')
def index():
    return f'home page user_id={g.user_id}'


# 强制登录视图
@app.route('/profile')
@login_required
def get_user_profile():
    return f'user profile page user_id={g.user_id}'


# 模拟登录
@app.route('/login')
def set_cookie():
    resp = make_response('set cookie ok')
    # resp.set_cookie('cookie名字', 'cookie值', max_age=有效时间)
    resp.set_cookie('user_id', '123456', max_age=3600)  # 设置有效期
    return resp


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值