flask是一个非常轻量化的后端框架,与django相比,它拥有更加简洁的框架。django功能全而强大,它内置了很多库包括路由,表单,模板,基本数据库管理等。flask框架只包含了两个核心库(Jinja2 模板引擎和 Werkzeug WSGI 工具集),需要什么库只需要外部引入即可,让开发者更随心所欲的开发应用
Flask文档
中文文档(http://docs.jinkan.org/docs/flask)
英文文档(http://flask.pocoo.org/docs/1.0/)
mkvirtualenv flask -p python3
workon flask
pip install flask
# 导入Flask类
from flask import Flask
#Flask类接收一个参数__name__
app = Flask(__name__)
# 装饰器的作用是将路由映射到视图函数index
@app.route('/')
def index():
return 'Hello World'
# Flask应用程序实例的run方法启动WEB服务器
if __name__ == '__main__':
app.run()
python test.py
Flask 程序实例在创建的时候,需要默认传入当前 Flask 程序所指定的包(模块)
Flask 应用程序在创建的时候一些需要我们关注的参数:
import_name
Flask程序所在的包(模块),传__name__就可以
其可以决定 Flask 在访问静态文件时查找的路径
static_url_path
静态文件访问路径,可以不传,默认为:/ + static_folder
static_folder
静态文件存储的文件夹,可以不传,默认为 static
template_folder
模板文件存储的文件夹,可以不传,默认为 templates
默认参数情况
app = Flask(__name__)
文件目录
访问http://127.0.0.1:5000/static/fu.png就可以访问到图片
完整参数情况下
# 导入Flask类
from flask import Flask
#Flask类接收一个参数__name__,static_url_path是静态文件访问路径,static_folder静态文件存储的文件夹
app = Flask(__name__, static_url_path='w',static_folder='static_file')
# 装饰器的作用是将路由映射到视图函数index
@app.route('/')
def index():
return 'Hello World'
if __name__ == '__main__':
app.run()
文件目录
此时访问http://127.0.0.1:5000/w/IMG_5311.jpg,才可以访问到图片
2.应用程序配置参数
对于Flask对象初始化参数仅仅设置的是Flask本身的属性,比如:
Flask从哪里读取静态文件,Flask从哪里读取模板文件
还需要应用程序配置参数设置的,是一个Web应用工程的相关信息,比如:
数据库的连接信息,日志的配置信息,自定义的配置信息
使用方式
Flask将配置信息保存到了app.config属性中
读取
app.config.get(name)
app.config[name]
设置
test_config.py
# 导入Flask类
from flask import Flask
# 配置对象方式加载配置信息
class DefaultConfig(object):
"""
默认配置信息
"""
SECRET_KEY = 'FDDKKKGFHFHRHNGGGKLTUIRKTKJBNSDFFFWET'
#Flask类接收一个参数__name__
app = Flask(__name__, static_url_path='/w', static_folder='static_file')
# 设置
app.config.from_object(DefaultConfig)
#定义视图
@app.route('/')
def index():
# 读取配置信息
print(app.config['SECRET_KEY'])
return 'Hello World'
if __name__ == '__main__':
app.run()
运行程序在后台就会打印出来 SECRET_KEY
在当前目前创建一个setting.py文件,里面写入配置信息
SECRET_KEY = '我是配置文件信息'
test_config.py
# 导入Flask类
from flask import Flask
# 配置对象方式加载配置信息
class DefaultConfig(object):
"""
默认配置信息
"""
SECRET_KEY = 'FDDKKKGFHFHRHNGGGKLTUIRKTKJBNSDFFFWET'
#Flask类接收一个参数__name__
app = Flask(__name__, static_url_path='/w', static_folder='static_file')
# 设置
# app.config.from_object(DefaultConfig)
app.config.from_pyfile('setting.py')
#定义视图
@app.route('/')
def index():
# 读取配置信息
print(app.config['SECRET_KEY'])
return 'Hello World'
if __name__ == '__main__':
app.run()
运行就会打印出配置文件的信息
在Linux系统中设置和读取环境变量的方式
export 变量名=变量值 # 设置
echo $变量名 # 读取
# 例如
export MYCONFIG=python
echo $MYCONFIG
在windows系统里是
set 变量名=变量值
test_config.py
# 导入Flask类
from flask import Flask
# 配置对象方式加载配置信息
class DefaultConfig(object):
"""
默认配置信息
"""
SECRET_KEY = 'FDDKKKGFHFHRHNGGGKLTUIRKTKJBNSDFFFWET'
#Flask类接收一个参数__name__
app = Flask(__name__, static_url_path='/w', static_folder='static_file')
# 设置
# app.config.from_object(DefaultConfig)
# app.config.from_pyfile('setting.py')
# silent=False 表示不安静的处理,没有值时报错通知,默认为False
# silent=True 表示安静的处理,即使没有值也让Flask正常的运行下去
app.config.from_envvar('ENV_SETTING', silent=True)
#定义视图
@app.route('/')
def index():
# 读取配置信息
print(app.config['SECRET_KEY'])
return 'Hello World'
if __name__ == '__main__':
app.run()
Flask使用环境变量加载配置就是通过环境变量值找到配置文件,再读取配置文件的信息,其使用方式为
我用windows来演示
Pycharm设置环境变量
from flask import Flask
def create_flask_app(config):
"""
创建Flask应用
:param config: 配置对象
:return: Flask应用
"""
app = Flask(__name__)
app.config.from_object(config) # 先从配置对象中加载默认配置信息
# 从环境变量指向的配置文件中读取的配置信息会覆盖掉从配置对象中加载的同名参数
app.config.from_envvar("ENV_SETTING", silent=True)
return app
class DefaultConfig(object):
"""默认配置"""
SECRET_KEY = 'abcdefg'
class DevelopmentConfig(DefaultConfig):
DEBUG=True
# app = create_flask_app(DefaultConfig)
app = create_flask_app(DevelopmentConfig)
@app.route("/")
def index():
print(app.config['SECRET_KEY'])
return "hello world"
指定运行的主机IP地址,端口,是否开启调试模式
app.run(host="0.0.0.0", port=6000, debug = True)
hello.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "Hello, World!
"
export FLASK_APP=hello
flask run
Running on http://127.0.0.1:5000/
环境变量 FLASK_APP 指明flask的启动实例
flask run -h 0.0.0.0 -p 8000 绑定地址 端口
flask run --help获取帮助
生产模式与开发模式的控制
通过FLASK_ENV环境变量指明
export FLASK_ENV=production 运行在生产模式,未指明则默认为此方式
export FLASK_ENV=development运行在开发模式
flask routes
Endpoint Methods Rule # Endpoint: 视图函数名字,Rule: 路径
-------- ------- -----------------------
index GET /
static GET /static/
print(app.url_map)
for rule in app.url_map.iter_rules():
print('name={} path={}'.format(rule.endpoint, rule.rule))
# iter_rules返回一个列表
# rule.endpoint视图的名字
# rule.rule视图的路径
以json的方式返回应用内的所有路由信息
主视图,返回所有视图网址
"""
rules_iterator = app.url_map.iter_rules()
return json.dumps({rule.endpoint: rule.rule for rule in rules_iterator})
# json.dumps()是把python对象转换成json对象的一个过程,生成的是字符串
cors跨域解决 ,前端访问后端接口时,先发一个options请求,后端允许请求,option是询问接口的特征的,不涉及真实的业务数据,比如接口的允许方式,允许的请求源头,然后浏览器发真才发真正的GET请求
那django-cors是在中间件中拦截处理了option请求
methods参数可以自己指定一个接口的请求方式
@app.route("/hello", methods=["GET", "POST"])
def view_func_2():
return "hello"
from flask import Flask, Blueprint
app = Flask(__name__)
user_bp = Blueprint('user', __name__) # 1.创建一个蓝图对象
@user_bp.route('/hello') # 2.设置视图的时候,由蓝图对象的路由装饰器进行绑定
def index():
return "hello 444world"
app.register_blueprint(user_bp, url_prefix='/user') # 3.在应用对象上注册这个蓝图对象
url_prefix表示需要访问的路径/hello前加一个前缀user ,访问的路径:
http://127.0.0.1:5000/user/hello
目录(包)蓝图中使用
新建一个目录goods, 通常将创建蓝图对象放到Python包的__init__.py文件中
init.py
from flask import Blueprint
goods_bp = Blueprint('good', __name__)
from . import view
view.py
from . import goods_bp
@goods_bp.route('/goods')
def get_goods():
return "get goods"
主程序文件中 注册蓝图
from flask import Flask, Blueprint
user_bp = Blueprint('user', __name__)
app = Flask(__name__)
@user_bp.route('/hello')
def index():
return "hello 4444world"
app.register_blueprint(user_bp, url_prefix='/user')
from goods import goods_bp
app.register_blueprint(goods_bp)
目录结构
app.register_blueprint(user_bp, url_prefix='/user') # url_prefix蓝图的前缀
admin = Blueprint("admin",__name__,static_folder='static_admin') # static_folder设置蓝图的静态目录
admin = Blueprint("admin",__name__,static_folder='static_admin',static_url_path='/lib') # static_url_path是静态访问路径
admin = Blueprint('admin',__name__,template_folder='my_templates') # template_folder蓝图内部模板目录
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
比如
@app.route('/users/' )
def user_info(user_id):
print(type(user_id))
return 'hello user {}'.format(user_id)
@app.route('/users/' )
def user_info(user_id):
print(type(user_id))
return 'hello user {}'.format(user_id)
from werkzeug.routing import BaseConverter
class MobileConverter(BaseConverter):
"""
手机号格式
"""
regex = r'1[3-9]\d{9}'
# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: mobile
app.url_map.converters['mobile'] = MobileConverter
@app.route(('/send_code/' ))
def get_mobile(mobile_num):
return "get mobile number:{}".format(mobile_num)
不同位置的参数都存放在request的不同属性中
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求的数据,并转换为字符串 | * |
form | 记录请求中的表单数据 | MultiDict |
args | 记录请求中的查询参数 | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的报文 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件 | * |
from flask import Flask,request
app=Flask(__name__)
@app.route('/user')
def get_user():
channel = request.args.get('channel')
return "get user id {}".format(channel)
运行结果
from flask import Flask,request
app=Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload_image():
f = request.files['picture']
with open('./img.jpg', 'wb') as new_jpg:
new_jpg.write(f.read())
return 'ok'
postman测试,如下图
flask给我们封装了一个save方法 不需要with open方法
from flask import Flask,request
app=Flask(__name__)
@app.route('/upload', methods=['POST'])
def upload_image():
f = request.files['picture']
# with open('./img.jpg', 'wb') as new_jpg:
# new_jpg.write(f.read())
f.save('./image.jpg')
return 'ok'
在不同的场景里返回不同的响应信息
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
模板html内容
<h1>{{ user_name }}</h1>
<h1>{{ user_id}}</h1>
</body>
</html>
如图 templates模板文件夹下新建的一个index.html
后端视图
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
username = 'zhangsan'
userid = 12
return render_template('index.html', user_name=username, user_id=userid)
扩展:后面视图这样也可以
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
data = {'user_name':'zhangsan', 'user_id':12}
# return render_template('index.html', user_name=username, user_id=userid)
return render_template('index.html', **data)
# **data等价于 user_name='zhangsan', user_id=12
from flask import redirect
@app.route('/demo2')
def demo2():
return redirect('http://www.baidu.com')
from flask import jsonify
@app.route('/demo3')
def demo3():
json_dict = {
"user_id": 10,
"user_name": "laowang"
}
return jsonify(json_dict)
元组必须是 (response, status, headers) 的形式
@app.route('/article')
def demo4():
# return 'hello world 666', 560
# return 'hello world 666', 560, [('user_name', 'lisi')]
return 'hello world 666', 560, {'user_name': 'lisi'}
@app.route('/article2')
def demo5():
resp = make_response('make response测试')
resp.headers[“user_name”] = “zhangsan”
resp.status = “404 not found”
return resp
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/cookie')
def set_cookie():
resp = make_response('set cookie ok')
resp.set_cookie('username', 'wangwu')
return resp
浏览器里查看设置的信息
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/cookie')
def set_cookie():
resp = make_response('set cookie ok')
resp.set_cookie('username', 'wangwu', max_age=3600)
return resp
from flask import request
@app.route('/get_cookie')
def get_cookie():
resp = request.cookies.get('username')
return resp
from flask import request
@app.route('/delete_cookie')
def delete_cookie():
response = make_response('hello world')
response.delete_cookie('username')
return response
class DefaultConfig(object):
SECRET_KEY = 'ddkslslgcd;slslgdd 98877777dds'
app.config.from_object(DefaultConfig)
或者直接设置
app.secret_key='ddkslslgcd;slslgdd 98877777dds'
@app.route('/set_session')
def set_session():
session['username'] = 'lisi'
return 'ok'
@app.route('/get_session')
def get_session():
username = session.get('username')
return 'get session username:{}'.format(username)
abort(404) # 抛出状态码的话,只能抛出 HTTP 协议的错误状态码
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
return '除数不能为0'
@app.errorhandler(500)
def internal_server_error(e):
return '服务器搬家了'
请求钩子就是起到中间件或者中间层的作用
假如有多个中间层,每个中间层可以理解为一个类,当一个具体的请求过来是,先去找中间件,DJANGO的处理过程
# [ middleware1 -> Class Middleware1
# def pre_process
# def after_process(response)
# middleware2
# middleware3]
# 请求的处理过程 pre_process -> view -> after_process
# request 请求支持 处理流程
# middleware1.pre_process() -> middleware2.pre_process() -> middleware3.pre_process()
# -> view() -> middleware3.after_process() -> middleware2.after_process() -> middleware1.after_process() -> client
# 中间件处理 不区分具体是哪个视图 ,对所有视图通通生效
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"
# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
print("after_request")
response.headers["Content-Type"] = "application/json"
return response
# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(response):
print("teardown_request")
@app.route('/')
def index():
print('view 视图已执行')
return 'index'
if __name__ == '__main__':
app.run(debug=True)
Flask中有两种上下文,请求上下文和应用上下文
Flask中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的
# 导入Flask类
from flask import Flask, current_app
#Flask类接收一个参数__name__
app = Flask(__name__, static_url_path='/w', static_folder='static_file')
app.redis_cli='redis-cli 888'
# 装饰器的作用是将路由映射到视图函数index
#定义视图
@app.route('/')
def index():
return current_app.redis_cli
if __name__ == '__main__':
app.run()
g 作为 flask 程序全局的一个临时变量,充当中间媒介的作用,我们可以通过它在一次请求调用的多个函数间传递一些数据。每次请求都会重设这个变量。
示例
from flask import Flask, g
app = Flask(__name__)
def db_query():
user_id = g.user_id
user_name = g.user_name
print('user_id={} user_name={}'.format(user_id, user_name))
@app.route('/')
def get_user_profile():
g.user_id = 123
g.user_name = 'itcast'
db_query()
return 'hello world'
g对象与请求钩子的综合案例
from flask import Flask, abort, g
app = Flask(__name__)
@app.before_request
def authentication():
"""
利用before_request请求钩子,在进入所有视图前先尝试判断用户身份
:return:
"""
# TODO 此处利用鉴权机制(如cookie、session、jwt等)鉴别用户身份信息
# if 已登录用户,用户有身份信息
g.user_id = 123
# else 未登录用户,用户无身份信息
# g.user_id = None
def login_required(func):
def wrapper(*args, **kwargs):
if g.user_id is not None:
return func(*args, **kwargs)
else:
abort(401)
return wrapper
@app.route('/')
def index():
return 'home page user_id={}'.format(g.user_id)
@app.route('/profile')
@login_required
def get_user_profile():
return 'user profile page user_id={}'.format(g.user_id)
>>> from flask import Flask
>>> app = Flask('')
>>> app.redis_cli = 'redis client'
>>>
>>> from flask import current_app
>>> current_app.redis_cli # 错误,没有上下文环境
报错
>>> with app.app_context(): # 借助with语句使用app_context创建应用上下文
... print(current_app.redis_cli)
...
redis client
>>> from flask import Flask
>>> app = Flask('')
>>> request.args # 错误,没有上下文环境
报错
>>> environ = {'wsgi.version':(1,0), 'wsgi.input': '', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'SERVER_NAME': 'itcast server', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '80'} # 模拟解析客户端请求之后的wsgi字典数据
>>> with app.request_context(environ): # 借助with语句使用request_context创建请求上下文
... print(request.path)
...