Flask中文文档
Flask诞生于2010年,是Armin ronacher(人名)用Python语言基于Werkzeug工具箱编写的轻量级Web开发框架。它主要面向需求简单的小应用。
Flask本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login),都需要用第三方的扩展来实现。比如可以用Flask-extension加入ORM、窗体验证工具,文件上传、身份验证等。Flask没有默认使用的数据库,你可以选择MySQL,也可以用NoSQL。其 WSGI 工具箱采用 Werkzeug(路由模块) ,模板引擎则使用 Jinja2 。
可以说Flask框架的核心就是Werkzeug和Jinja2。
Python最出名的框架要数Django,此外还有Flask、Tornado等框架。虽然Flask不是最出名的框架,但是Flask应该算是最灵活的框架之一,这也是Flask受到广大开发者喜爱的原因。
流程
客户端将请求打包成HTTP的请求报文(HTTP协议格式的请求数据)
采用TCP传输发送给服务器端
服务器接收到请求报文后按照HTTP协议进行解析
服务器根据解析后获知的客户端请求进行逻辑执行
服务器将执行后的结果封装成HTTP的响应报文(HTTP协议格式的响应数据)
采用刚才的TCP连接将响应报文发送给客户端
客户端按照HTTP协议解析响应报文获取结果数据
能够被服务器调用起来,根据客户端的不同请求执行不同的逻辑处理形成要返回的数据的 程序
核心:实现路由和视图(业务逻辑处理)
Flask作为Web框架,它的作用主要是为了开发Web应用程序。那么我们首先来了解下Web应用程序。Web应用程序 (World Wide Web)诞生最初的目的,是为了利用互联网交流工作文档。
客户端不一定是浏览器,也可以是PC软件、手机APP、程序
根据服务器端的工作,将其分为两部分:
服务器与Python业务程序的配合使用WSGI协议
django提供了:
django-admin快速创建项目工程目录
manage.py 管理项目工程
orm模型(数据库抽象层)
admin后台管理站点
缓存机制
文件存储系统
用户认证系统
而这些,flask都没有,都需要扩展包来提供
Flask-SQLalchemy:操作数据库;
Flask-migrate:管理迁移数据库;
Flask-Mail:邮件;
Flask-WTF:表单;
Flask-script:插入脚本;
Flask-Login:认证用户状态;
Flask-RESTful:开发REST API的工具;
Flask-Bootstrap:集成前端Twitter Bootstrap框架;
Flask-Moment:本地化日期和时间;
pip install flask==版本
所有Flask程序必须有一个程序实例。
Flask调用视图函数后,会将视图函数的返回值作为响应的内容,返回给客户端。一般情况下,响应内容主要是字符串和状态码。
当客户端想要获取资源时,一般会通过浏览器发起HTTP请求。此时,Web服务器使用WSGI(Web Server Gateway Interface)协议,把来自客户端的所有请求都交给Flask程序实例。WSGI是为 Python 语言定义的Web服务器和Web应用程序之间的一种简单而通用的接口,它封装了接受HTTP请求、解析HTTP请求、发送HTTP,响应等等的这些底层的代码和操作,使开发者可以高效的编写Web应用。
程序实例使用Werkzeug来做路由分发(URL请求和视图函数之间的对应关系)。根据每个URL请求,找到具体的视图函数。 在Flask程序中,路由的实现一般是通过程序实例的route装饰器实现。route装饰器内部会调用add_url_route()方法实现路由注册。
调用视图函数,获取响应数据后,把数据传入HTML模板文件中,模板引擎负责渲染响应数据,然后由Flask返回响应数据给浏览器,最后浏览器处理返回的结果显示给客户端。
创建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()
启动运行
手动运行
python helloworld.py
pycharm 运行
像正常运行普通python程序一样即可。
Flask 程序实例在创建的时候,需要默认传入当前 Flask 程序所指定的包(模块),接下来就来详细查看一下 Flask 应用程序在创建的时候一些需要我们关注的参数:
import_name
其可以决定 Flask 在访问静态文件时查找的路径
Flask程序所在的包(模块),传 __name__ 就可以
static_url_path
静态文件访问路径,可以不传,默认为:/ + static_folder
static_folder
静态文件存储的文件夹,可以不传,默认为 static
template_folder
模板文件存储的文件夹,可以不传,默认为 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才可以访问到图片
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的说明:
表示系统环境变量中没有设置相应值时是否抛出异常
False 表示不安静的处理,没有值时报错通知,默认为False
True 表示安静的处理,即时没有值也让Flask正常的运行下去
Pycharm运行时设置环境变量的方式
应用场景:
配置文件的地址不固定;
在代码中不想暴露真实的配置文件地址,只在运行代码的服务器上才有真实配置文件的信息。
可以指定运行的主机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()
@app.route("/")
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))
@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 中,定义路由其默认的请求方式为:
@app.route("/hello1", methods=["POST"])
def view_func_1():
return "hello world 1"
@app.route("/hello2", methods=["GET", "POST"])
def view_func_2():
return "hello world 2"
在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
| |--- ...
|...
在应用中注册蓝图时使用url_prefix参数指定
app.register_blueprint(user_bp, url_prefix='/user')
app.register_blueprint(goods_bp, url_prefix='/goods')
和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在 创建时指定 static_folder 参数。
下面的示例将蓝图所在目录下的static_admin目录设置为静态目录
admin = Blueprint("admin",__name__,static_folder='static_admin')
app.register_blueprint(admin,url_prefix='/admin')
现在就可以使用/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')
蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用 template_folder 关键字参数设置模板目录
admin = Blueprint('admin',__name__,template_folder='my_templates')
请求携带的数据可能出现在HTTP报文中的不同位置,需要使用不同的方法来获取参数。
URL路径参数(动态路由)
例如,有一个请求访问的接口地址为/users/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为参数名传入视图。
Flask也提供其他类型的转换器
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)
自定义转换器主要做3步
from werkzeug.routing import BaseConverter
class MobileConverter(BaseConverter):
"""
手机号格式
"""
regex = r'1[3-9]\d{9}'
注意regex名字固定
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的不同属性中
例如 获取请求/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
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
我的模板html内容
<br/>{{ my_str }}
<br/>{{ my_int }}
body>
html>
后端视图
from flask import render_template
@app.route('/')
def index():
mstr = 'Hello'
mint = 123
return render_template('index.html', my_str=mstr, my_int=mint)
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) 的形式,且至少包含一个元素。 status 值会覆盖状态代码, headers 可以是一个列表或字典,作为额外的消息标头值。
@app.route('/demo4')
def demo4():
# return '状态码为 666', 666
# return '状态码为 666', 666, [('hello', 'Python')]
return '状态码为 666', 666, {'hello': 'Python'}
@app.route('/demo5')
def demo5():
resp = make_response('make response测试')
resp.headers[“hello”] = “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', 'python')
return resp
设置有效期
@app.route('/cookie')
def set_cookie():
response = make_response('hello world')
response.set_cookie('username', 'python', 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'] = 'python'
return 'set session ok'
@app.route('/get_session')
def get_session():
username = session.get('username')
return 'get session username {}'.format(username)
flask将session数据保存在浏览器中
django将session数据保存在数据库中
abort 方法
参数:
code – HTTP的错误状态码
# 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)
在第1次请求时的打印:
before_first_request
before_request
after_request
teardown_request
在第2次请求时的打印:
before_request
after_request
teardown_request
from flask import Flask
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 called')
return 'index'
if __name__ == '__main__':
app.run(debug=True)
上下文:即语境,语意,在程序中可以理解为在代码执行到某一时刻时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情。
Flask中有两种上下文,请求上下文和应用上下文
Flask中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。
在 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 = 'python'
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)
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)
...
/
安装
pip install flask-restful
Hello World
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()
使用步骤:
args.rate
或
args['rate']
required
描述请求是否一定要携带对应参数,默认值为False
class DemoResource(Resource):
def get(self):
rp = RequestParser()
rp.add_argument('a', required=False)
args = rp.parse_args()
return {'msg': 'data={}'.format(args.a)}
help
参数检验错误时返回的错误描述信息
rp.add_argument('a', required=True, help='missing a param')
action
描述对于请求参数中出现多个同名参数时的处理方式
rp.add_argument('a', required=True, help='missing a param', action='append')
type
描述参数应该匹配的类型,可以使用python的标准数据类型string、int,也可使用Flask-RESTful提供的检验方法,还可以自己定义
rp.add_argument('a', type=int, required=True, help='missing a param', action='append')
from flask_restful import inputs
rp.add_argument('a', type=inputs.regex(r'^\d{2}&'))
natural 自然数0、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)
location
描述参数应该在请求数据中出现的位置
# 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': 'python'}
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
为满足返回{“message”: “OK”, “data”: {‘user_id’:1, ‘name’: ‘python’}}需求,做如下改动即可
@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