2018 Python官方年度调研报告显示Flask与Django为Python Web开发使用最主要的两个框架。
Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。
Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都需要用第三方的扩展来实现。比如可以用 Flask 扩展加入ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。
其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。
最新版本 1.0.2
1) 框架轻重
重量级的框架:为方便业务程序的开发,提供了丰富的工具、组件,如Django
轻量级的框架:只提供Web框架的核心功能,自由、灵活、高度定制,如Flask、Tornado
2)与Django对比
django提供了:
django-admin快速创建项目工程目录
manage.py 管理项目工程
orm模型(数据库抽象层)
admin后台管理站点
缓存机制
文件存储系统
用户认证系统
而这些,flask都没有,都需要扩展包来提供
扩展列表:http://flask.pocoo.org/extensions/
============================================================
# 虚拟环境
mkvirtualenv # 创建虚拟环境
rmvirtualenv # 删除虚拟环境
workon # 进入虚拟环境、查看所有虚拟环境
deactivate # 退出虚拟环境
# pip
pip install # 安装依赖包
pip uninstall # 卸载依赖包
pip list # 查看已安装的依赖包
pip freeze # 冻结当前环境的依赖包
mkvirtualenv flask -p python3
注意需要联网
使用flask 1.0.2版本,注意需要联网
pip install flask
=============================================
掌握flask程序的编写方式
回忆如何起步编写Django程序?
创建helloworld.py文件
# 导入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()
4 启动运行
手动运行
python helloworld.py
pycharm 运行
像正常运行普通python程序一样即可。
===============================================
Flask 程序实例在创建的时候,需要默认传入当前 Flask 程序所指定的包(模块),接下来就来详细查看一下 Flask 应用程序在创建的时候一些需要我们关注的参数:
__name__
就可以/ + static_folder
static
templates
默认参数情况下
app = Flask(__name__)
文件目录
----
|---static
| |--- 1.png
|---helloworld.py
访问 127.0.0.1:5000/static/1.png
就可以访问到图片
修改参数的情况下
app = Flask(__name__, static_url_path='/url_path_param', static_folder='folder_param')
文件目录
----
|---folder_param # 此处目录名变化
| |--- 1.png
|---helloworld.py
访问127.0.0.1:5000/url_path_param/1.png
才可以访问到图片
对于Flask对象初始化参数仅仅设置的是Flask本身的属性,比如:
等等。
应用程序配置参数设置的是一个Web应用工程的相关信息,比如:
等等
集中管理项目的所有配置信息
Django将所有配置信息都放到了settings.py文件中,而Flask则不同。
Flask将配置信息保存到了app.config
属性中,该属性可以按照字典类型进行操作。
读取
app.config.get(name)
app.config[name]
设置
主要使用以下三种方式:
从配置对象中加载
app.config.from_object(配置对象)
class DefaultConfig(object):
"""默认配置"""
SECRET_KEY = 'TPmi4aLWRbyVq8zu9v82dWYW1'
app = Flask(__name__)
app.config.from_object(DefaultConfig)
@app.route("/")
def index():
print(app.config['SECRET_KEY'])
return "hello world"
应用场景:
作为默认配置写在程序代码中
可以继承
class DevelopmentConfig(DefaultConfig):
DEBUG=True
从配置文件中加载
app.config.from_pyfile(配置文件)
新建一个配置文件setting.py
SECRET_KEY = 'TPmi4aLWRbyVq8zu9v82dWYW1'
在Flask程序文件中
app = Flask(__name__)
app.config.from_pyfile('setting.py')
@app.route("/")
def index():
print(app.config['SECRET_KEY'])
return "hello world"
应用场景:
在项目中使用固定的配置文件
从环境变量中加载
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。 环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息。
通俗的理解,环境变量就是我们设置在操作系统中,由操作系统代为保存的变量值
在Linux系统中设置和读取环境变量的方式如下:
export 变量名=变量值 # 设置
echo $变量名 # 读取
# 例如
export ITCAST=python
echo $ITCAST
Flask使用环境变量加载配置的本质是通过环境变量值找到配置文件,再读取配置文件的信息,其使用方式为
app.config.from_envvar('环境变量名')
环境变量的值为配置文件的绝对路径
先在终端中执行如下命令
export PROJECT_SETTING='~/setting.py'
再运行如下代码
app = Flask(__name__)
app.config.from_envvar('PROJECT_SETTING', silent=True)
@app.route("/")
def index():
print(app.config['SECRET_KEY'])
return "hello world"
关于silent
的说明:
表示系统环境变量中没有设置相应值时是否抛出异常
Pycharm运行时设置环境变量的方式
应用场景:
配置文件的地址不固定;
在代码中不想暴露真实的配置文件地址,只在运行代码的服务器上才有真实配置文件的信息。
使用工厂模式创建Flask app,并结合使用配置对象与环境变量加载配置
def create_flask_app(config):
"""
创建Flask应用
:param config: 配置对象
:return: Flask应用
"""
app = Flask(__name__)
app.config.from_object(config)
# 从环境变量指向的配置文件中读取的配置信息会覆盖掉从配置对象中加载的同名参数
app.config.from_envvar("PROJECT_SETTING", silent=True)
return app
class DefaultConfig(object):
"""默认配置"""
SECRET_KEY = 'itcast1'
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=5000, debug = True)
关于DEBUG调试模式
========================================
在1.0版本之后,Flask调整了开发服务器的启动方式,由代码编写app.run()
语句调整为命令flask run
启动。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
# 程序中不用再写app.run()
$ export FLASK_APP=helloworld
$ 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
运行在开发模式$ export FLASK_APP=helloworld
$ python -m flask run
* Running on http://127.0.0.1:5000/
设置环境变量
旧版本Pycharm设置
=====================================
@app.route("/itcast")
def view_func():
return "hello world"
命令行方式
flask routes
Endpoint Methods Rule
-------- ------- -----------------------
index GET /
static GET /static/
在程序中获取
在应用中的url_map属性中保存着整个Flask应用的路由映射信息,可以通过读取这个属性获取路由信息
print(app.url_map)
如果想在程序中遍历路由信息,可以采用如下方式
for rule in app.url_map.iter_rules():
print('name={} path={}'.format(rule.endpoint, rule.rule))
需求
通过访问/
地址,以json的方式返回应用内的所有路由信息
实现
@app.route('/')
def route_map():
"""
主视图,返回所有视图网址
"""
rules_iterator = app.url_map.iter_rules()
return json.dumps({rule.endpoint: rule.rule for rule in rules_iterator})
在 Flask 中,定义路由其默认的请求方式为:
利用methods
参数可以自己指定一个接口的请求方式
@app.route("/itcast1", methods=["POST"])
def view_func_1():
return "hello world 1"
@app.route("/itcast2", methods=["GET", "POST"])
def view_func_2():
return "hello world 2"
在一个Flask 应用项目中,如果业务视图过多,可否将以某种方式划分出的业务单元单独维护,将每个单元用到的视图、静态文件、模板文件等独立分开?
例如从业务角度上,可将整个应用划分为用户模块单元、商品模块单元、订单模块单元,如何分别开发这些不同单元,并最终整合到一个项目应用中?
在Django中这种需求是如何实现的?
在Flask中,使用蓝图Blueprint来分模块组织管理。
蓝图实际可以理解为是一个存储一组视图方法的容器对象,其具有如下特点:
但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。
使用蓝图可以分为三个步骤
创建一个蓝图对象
user_bp=Blueprint('user',__name__)
在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器
@user_bp.route('/')
def user_profile():
return 'user_profile'
在应用对象上注册这个蓝图对象
app.register_blueprint(user_bp)
单文件蓝图
可以将创建蓝图对象与定义视图放到一个文件中 。
目录(包)蓝图
对于一个打算包含多个文件的蓝图,通常将创建蓝图对象放到Python包的__init__.py
文件中
--------- project # 工程目录
|------ main.py # 启动文件
|------ user #用户蓝图
| |--- __init__.py # 此处创建蓝图对象
| |--- passport.py
| |--- profile.py
| |--- ...
|
|------ goods # 商品蓝图
| |--- __init__.py
| |--- ...
|...
1 指定蓝图的url前缀
在应用中注册蓝图时使用url_prefix
参数指定
app.register_blueprint(user_bp, url_prefix='/user')
app.register_blueprint(goods_bp, url_prefix='/goods')
2 蓝图内部静态文件
和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在 创建时指定 static_folder 参数。
下面的示例将蓝图所在目录下的static_admin目录设置为静态目录
admin = Blueprint("admin",__name__,static_folder='static_admin')
app.register_blueprint(admin,url_prefix='/admin')
现在就可以使用/admin/static_admin/
访问static_admin
目录下的静态文件了。
也可通过static_url_path
改变访问路径
admin = Blueprint("admin",__name__,static_folder='static_admin',static_url_path='/lib')
app.register_blueprint(admin,url_prefix='/admin')
3 蓝图内部模板目录
蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用 template_folder 关键字参数设置模板目录
admin = Blueprint('admin',__name__,template_folder='my_templates')
===========================================
在视图编写中需要读取客户端请求携带的数据时,如何才能正确的取出数据呢?
请求携带的数据可能出现在HTTP报文中的不同位置,需要使用不同的方法来获取参数。
例如,有一个请求访问的接口地址为/users/123
,其中123实际上为具体的请求参数,表明请求123号用户的信息。此时如何从url中提取出123的数据?
Flask不同于Django直接在定义路由时编写正则表达式的方式,而是采用转换器语法:
@app.route('/users/')
def user_info(user_id):
print(type(user_id))
return 'hello user {}'.format(user_id)
此处的<>
即是一个转换器,默认为字符串类型,即将该位置的数据以字符串格式进行匹配、并以字符串为数据类型类型、 user_id
为参数名传入视图。
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)
如果遇到需要匹配提取/sms_codes/18512345678
中的手机号数据,Flask内置的转换器就无法满足需求,此时需要自定义转换器。
定义方法
自定义转换器主要做3步
创建转换器类,保存匹配时的正则表达式
from werkzeug.routing import BaseConverter
class MobileConverter(BaseConverter):
"""
手机号格式
"""
regex = r'1[3-9]\d{9}'
regex
名字固定将自定义的转换器告知Flask应用
app = Flask(__name__)
# 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: mobile
app.url_map.converters['mobile'] = MobileConverter
在使用转换器的地方定义使用
@app.route('/sms_codes/')
def send_sms_code(mob_num):
return 'send sms code to {}'.format(mob_num)
如果想要获取其他地方传递的参数,可以通过Flask提供的request对象来读取。
不同位置的参数都存放在request的不同属性中
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求的数据,并转换为字符串 | * |
form | 记录请求中的表单数据 | MultiDict |
args | 记录请求中的查询参数 | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的报文头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件 | * |
例如 想要获取请求/articles?channel_id=1
中channel_id
的参数,可以按如下方式使用:
from flask import request
@app.route('/articles')
def get_articles():
channel_id = request.args.get('channel_id')
return 'you wanna get articles of channel {}'.format(channel_id)
上传图片
客户端上传图片到服务器,并保存到服务器中
from flask import request
@app.route('/upload', methods=['POST'])
def upload_file():
f = request.files['pic']
# with open('./demo.png', 'wb') as new_file:
# new_file.write(f.read())
f.save('./demo.png')
return 'ok'
=============================================
如何在不同的场景里返回不同的响应信息?
使用render_template
方法渲染模板并返回
例如,新建一个模板index.html
Title
我的模板html内容
{{ my_str }}
{{ my_int }}
后端视图
from flask import render_template
@app.route('/')
def index():
mstr = 'Hello 黑马程序员'
mint = 10
return render_template('index.html', my_str=mstr, my_int=mint)
from flask import redirect
@app.route('/demo2')
def demo2():
return redirect('http://www.itheima.com')
from flask import jsonify
@app.route('/demo3')
def demo3():
json_dict = {
"user_id": 10,
"user_name": "laowang"
}
return jsonify(json_dict)
1) 元祖方式
可以返回一个元组,这样的元组必须是 (response, status, headers) 的形式,且至少包含一个元素。 status 值会覆盖状态代码, headers 可以是一个列表或字典,作为额外的消息标头值。
@app.route('/demo4')
def demo4():
# return '状态码为 666', 666
# return '状态码为 666', 666, [('Itcast', 'Python')]
return '状态码为 666', 666, {'Itcast': 'Python'}
2) make_response方式
@app.route('/demo5')
def demo5():
resp = make_response('make response测试')
resp.headers[“Itcast”] = “Python”
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', 'itcast')
return resp
设置有效期
@app.route('/cookie')
def set_cookie():
response = make_response('hello world')
response.set_cookie('username', 'itheima', max_age=3600)
return response
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
需要先设置SECRET_KEY
class DefaultConfig(object):
SECRET_KEY = 'fih9fh9eh9gh2'
app.config.from_object(DefaultConfig)
或者直接设置
app.secret_key='xihwidfw9efw'
from flask import session
@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 'get session username {}'.format(username)
flask将session数据保存到了哪里?
==========================================
# abort(404)
abort(500)
抛出状态码的话,只能抛出 HTTP 协议的错误状态码
@app.errorhandler(500)
def internal_server_error(e):
return '服务器搬家了'
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
return '除数不能为0'
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
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():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
before_first_request
before_request
after_request
teardown_request
before_request
after_request
teardown_request
上下文:即语境,语意,在程序中可以理解为在代码执行到某一时刻时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情。
Flask中有两种上下文,请求上下文和应用上下文
Flask中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。
思考:在视图函数中,如何取到当前请求的相关数据?比如:请求地址,请求方式,cookie等等
在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、session
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。
应用上下文对象有:current_app,g
应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:
示例
创建current_app_demo.py
from flask import Flask, current_app
app1 = Flask(__name__)
app2 = Flask(__name__)
# 以redis客户端对象为例
# 用字符串表示创建的redis客户端
# 为了方便在各个视图中使用,将创建的redis客户端对象保存到flask app中,
# 后续可以在视图中使用current_app.redis_cli获取
app1.redis_cli = 'app1 redis client'
app2.redis_cli = 'app2 redis client'
@app1.route('/route11')
def route11():
return current_app.redis_cli
@app1.route('/route12')
def route12():
return current_app.redis_cli
@app2.route('/route21')
def route21():
return current_app.redis_cli
@app2.route('/route22')
def route22():
return current_app.redis_cli
运行
export FLASK_APP=current_app_demo:app1
flask run
/route11
显示app1 redis client
/route12
显示app1 redis client
export FLASK_APP=current_app_demo:app2
flask run
/route21
显示app2 redis client
/route22
显示app2 redis client
作用
current_app
就是当前运行的flask app,在代码不方便直接操作flask的app对象时,可以操作current_app
就等价于操作flask app对象
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'
需求
实现
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)
思考
在Flask程序未运行的情况下,调试代码时需要使用current_app
、g
、request
这些对象,会不会有问题?该如何使用?
app_context
app_context
为我们提供了应用上下文环境,允许我们在外部使用应用上下文current_app
、g
可以通过with
语句进行使用
>>> 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
request_context
request_context
为我们提供了请求上下文环境,允许我们在外部使用请求上下文request
、session
可以通过with语句进行使用
>>> 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)
...
/
=================================
Flask-RESTful是用于快速构建REST API的Flask扩展。
pip install flask-restful
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class HelloWorldResource(Resource):
def get(self):
return {'hello': 'world'}
def post(self):
return {'msg': 'post hello world'}
api.add_resource(HelloWorldResource, '/')
# 此处启动对于1.0之后的Flask可有可无
if __name__ == '__main__':
app.run(debug=True)
通过endpoint参数为路由起名
api.add_resource(HelloWorldResource, '/', endpoint='HelloWorld')
from flask import Flask, Blueprint
from flask_restful import Api, Resource
app = Flask(__name__)
user_bp = Blueprint('user', __name__)
user_api = Api(user_bp)
class UserProfileResource(Resource):
def get(self):
return {'msg': 'get user profile'}
user_api.add_resource(UserProfileResource, '/users/profile')
app.register_blueprint(user_bp)
使用method_decorators
添加装饰器
def decorator1(func):
def wrapper(*args, **kwargs):
print('decorator1')
return func(*args, **kwargs)
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print('decorator2')
return func(*args, **kwargs)
return wrapper
class DemoResource(Resource):
method_decorators = [decorator1, decorator2]
def get(self):
return {'msg': 'get view'}
def post(self):
return {'msg': 'post view'}
class DemoResource(Resource):
method_decorators = {
'get': [decorator1, decorator2],
'post': [decorator1]
}
# 使用了decorator1 decorator2两个装饰器
def get(self):
return {'msg': 'get view'}
# 使用了decorator1 装饰器
def post(self):
return {'msg': 'post view'}
# 未使用装饰器
def put(self):
return {'msg': 'put view'}
Flask-RESTful 提供了RequestParser
类,用来帮助我们检验和转换请求数据。
from flask_restful import reqparse
parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate cannot be converted', location='args')
parser.add_argument('name')
args = parser.parse_args()
使用步骤:
创建RequestParser
对象
向RequestParser
对象中添加需要检验或转换的参数声明
使用parse_args()
方法启动检验处理
检验之后从检验结果中获取参数时可按照字典操作或对象属性操作
args.rate
或
args['rate']
描述请求是否一定要携带对应参数,默认值为False
True 强制要求携带
若未携带,则校验失败,向客户端返回错误信息,状态码400
False 不强制要求携带
若不强制携带,在客户端请求未携带参数时,取出值为None
class DemoResource(Resource):
def get(self):
rp = RequestParser()
rp.add_argument('a', required=False)
args = rp.parse_args()
return {'msg': 'data={}'.format(args.a)}
参数检验错误时返回的错误描述信息
rp.add_argument('a', required=True, help='missing a param')
描述对于请求参数中出现多个同名参数时的处理方式
action='store'
保留出现的第一个, 默认action='append'
以列表追加保存所有同名参数的值rp.add_argument('a', required=True, help='missing a param', action='append')
描述参数应该匹配的类型,可以使用python的标准数据类型string、int,也可使用Flask-RESTful提供的检验方法,还可以自己定义
标准类型
rp.add_argument('a', type=int, required=True, help='missing a param', action='append')
Flask-RESTful提供
检验类型方法在flask_restful.inputs
模块中
url
regex(指定正则表达式)
from flask_restful import inputs
rp.add_argument('a', type=inputs.regex(r'^\d{2}&'))
natural
自然数0、1、2、3...
positive
正整数 1、2、3...
int_range(low ,high)
整数范围
rp.add_argument('a', type=inputs.int_range(1, 10))
boolean
自定义
def mobile(mobile_str):
"""
检验手机号格式
:param mobile_str: str 被检验字符串
:return: mobile_str
"""
if re.match(r'^1[3-9]\d{9}$', mobile_str):
return mobile_str
else:
raise ValueError('{} is not a valid mobile'.format(mobile_str))
rp.add_argument('a', type=mobile)
描述参数应该在请求数据中出现的位置
# Look only in the POST body
parser.add_argument('name', type=int, location='form')
# Look only in the querystring
parser.add_argument('PageSize', type=int, location='args')
# From the request headers
parser.add_argument('User-Agent', location='headers')
# From http cookies
parser.add_argument('session_id', location='cookies')
# From json
parser.add_argument('user_id', location='json')
# From file uploads
parser.add_argument('picture', location='files')
也可指明多个位置
parser.add_argument('text', location=['headers', 'json'])
Flask-RESTful 提供了marshal工具,用来帮助我们将数据序列化为特定格式的字典数据,以便作为视图的返回值。
from flask_restful import Resource, fields, marshal_with
resource_fields = {
'name': fields.String,
'address': fields.String,
'user_id': fields.Integer
}
class Todo(Resource):
@marshal_with(resource_fields, envelope='resource')
def get(self, **kwargs):
return db_get_todo()
也可以不使用装饰器的方式
class Todo(Resource):
def get(self, **kwargs):
data = db_get_todo()
return marshal(data, resource_fields)
示例
# 用来模拟要返回的数据对象的类
class User(object):
def __init__(self, user_id, name, age):
self.user_id = user_id
self.name = name
self.age = age
resoure_fields = {
'user_id': fields.Integer,
'name': fields.String
}
class Demo1Resource(Resource):
@marshal_with(resoure_fields, envelope='data1')
def get(self):
user = User(1, 'itcast', 12)
return user
class Demo2Resource(Resource):
def get(self):
user = User(1, 'itcast', 12)
return marshal(user, resoure_fields, envelope='data2')
想要接口返回的JSON数据具有如下统一的格式
{"message": "描述信息", "data": {要返回的具体数据}}
在接口处理正常的情况下, message返回ok即可,但是若想每个接口正确返回时省略message字段
class DemoResource(Resource):
def get(self):
return {'user_id':1, 'name': 'itcast'}
对于诸如此类的接口,能否在某处统一格式化成上述需求格式?
{"message": "OK", "data": {'user_id':1, 'name': 'itcast'}}
Flask-RESTful的Api对象提供了一个representation
的装饰器,允许定制返回数据的呈现格式
api = Api(app)
@api.representation('application/json')
def handle_json(data, code, headers):
# TODO 此处添加自定义处理
return resp
Flask-RESTful原始对于json的格式处理方式如下:
代码出处:flask_restful.representations.json
from flask import make_response, current_app
from flask_restful.utils import PY3
from json import dumps
def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
settings = current_app.config.get('RESTFUL_JSON', {})
# If we're in debug mode, and the indent is not set, we set it to a
# reasonable value here. Note that this won't override any existing value
# that was set. We also set the "sort_keys" value.
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3)
# always end the json dumps with a new line
# see https://github.com/mitsuhiko/flask/pull/1262
dumped = dumps(data, **settings) + "\n"
resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp
为满足需求,做如下改动即可
@api.representation('application/json')
def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
# 此处为自己添加***************
if 'message' not in data:
data = {
'message': 'OK',
'data': data
}
# **************************
settings = current_app.config.get('RESTFUL_JSON', {})
# If we're in debug mode, and the indent is not set, we set it to a
# reasonable value here. Note that this won't override any existing value
# that was set. We also set the "sort_keys" value.
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3)
# always end the json dumps with a new line
# see https://github.com/mitsuhiko/flask/pull/1262
dumped = dumps(data, **settings) + "\n"
resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp