赞
踩
目录
4、demo2示例--flask_restful方式实现路由
5、app.route()和api.add_resource()区别
2、from_object方法直接从同目录下python配置文件模块读取配置
4、from_object()和from_pyfile()的区别
1、Django
Django如此知名,很大程度上是因为提供了非常齐备的官方文档,它提供了一站式的解决方案,包含缓存、ORM、管理后台、验证、表单处理等,使得开发复杂的数据库驱动的网站变得很简单。
但正因为它坚持自己对于Web框架的理解,系统耦合度太高,替换掉内置的功能往往需要花费一些功夫,所以学习曲线也相当陡峭。
2、Flask
Flask是一个轻量级Web应用框架,它基于Werkzeug实现的WSGI和Jinja2模板引擎。
它的设计哲学和Django不同:只保留核心,通过扩展机制来增加其他功能。
3、Tornado
Tornado全称Tornado Web Server,最初是由FriendFeed开发的非阻塞式Web服务器,现在我们看到的是被Fackbook收购后开源出来的版本。
它和其他主流框架有个明显的区别:它是非阻塞式服务器,而且速度相当快。
得益于其非阻塞的方式和对epoll的运用,Tornado每秒可以处理数以千计的连接,这意味着对于长轮询、WebSocket等实时Web服务来说, Tornado是一个理想的Web框架。
4、如何选择
1)选择更主流的框架。
因为它们的文档更齐全,技术积累要更多,能得到更好的支持。
2)确认选择的框架是否足够满足需求。
没有最好的框架,只有最合适的框架。你所选择的Web框架不仅需要满足当前的需求,也要充分考虑项目发展一段时间之后的情况,即前瞻性。
Flask本身尽量保持了内核的精简,其设计初衷就是不会替开发者做太多决策。
# pip install Flask
# pip install flask-restful
保存为 hello.py,运行即可
- from flask import Flask
- app = Flask(__name__)
-
- @app.route('/')
- def hello_world():
- return 'Hello, World!'
-
- if__name__=='__main__':
- app.run(host='0.0.0.0', port=9000)
解析:
1)导入了Flask类,该类的实例将会成为WSGI应用。
2)创建一个该类的实例,第一个参数是应用模块或者包的名称。一般使用__name__,这个参数是必需的,这样Flask才能知道在哪里可以找到模板和静态文件等东西。
3)使用route()装饰器来告诉Flask触发函数的URL。 函数名称被用于生成相关联的URL。函数最后返回需要在用户浏览器中显示的信息。
4)执行app.run就可以启动服务。默认Flask只监听本地127.0.0.1地址,端口为5000。可以自定义指定host和port参数,0.0.0.0表示监听所有地址,这样就可以在本机访问。
运行:
- $ python hello.py
- * Running on http://127.0.0.1:9000/
- * Restarting with reloader
打开一个新的窗口,使用curl工具测试API:
- $ curl http://127.0.0.1:9000/
- {"hello": "world"}
Flask-RESTful 是一个可以简化 APIs 的构建的 Flask 扩展,提供了一个 Resource 基础类,通过api.add_resource()方式实现路由,能够定义一个给定URL的一个或者多个HTTP方法。
例如,定义一个可以使用 HTTP 的 GET, PUT 以及 DELETE 方法的 User 资源。
- from flask import Flask
- from flask_restful import Resource, Api
- app = Flask(__name__)
- api = Api(app)
- class UserAPI(Resource):
- def get(self, id):
- pass
- def put(self, id):
- pass
- def delete(self, id):
- pass
- api.add_resource(UserAPI, '/users/<int:id>', endpoint = 'user')
解析:
1)使用flask_restful,需要pip安装flask-restful
2)实例化flask-restful组件的Api类,调用其方法add_resource添加资源,并在对应资源中实现增删改查等HTTP方法。
add_resource(resource, *urls, **kwargs)
参数描述:
resource (Type[Resource]):自定义resource的类名class name,该类继承Resource基类,其成员函数定义了不同的HTTP请求方法的逻辑。
urls (str):resource使用的参数,可以匹配一个或多个url路由。
flask框架默认的路由和视图函数映射规则,是通过在视图函数上直接添加路由装饰器来实现的,这使得路由和视图函数的对应关系变得清晰,
但是当路由接口足够多的时候,可读性会变差。也不便于统一管理。flask_restful可以使我们像Django那样统一在一个地方设计所有的API规则。
优先考虑使用flask_restful。
字面意思,就是路径指向。主要用来解析路径,定义某某某从哪来、到哪去。
在网络中,路由指的是根据上一接口的数据包中的IP地址,查询路由表转发到另一个接口,它决定的是一个端到端的网络路径。
在web应用中,路由的作用,就是根据客户端发送的url,寻找对应的处理函数。
客户端的请求是以URL的形式传递给服务器的,处理URL和python函数之间映射关系的程序,就称为路由。
使用 route() 装饰器来把函数绑定到 URL,当请求url后缀为’/hello’时,会自动跳转到对应的hello()方法,如下:
- @app.route('/hello')
- def hello():
- return 'Hello, World'
url中的斜杠
url尾部有斜杠,如同一个文件夹,若未添加可自动补充。 URL没有尾部斜杠,其行为表现与一个文件类似,若添加了会报错404。
- @app.route('/hello/')
- def hello():
- return 'Hello, World'
使用flask-restful的Api.add_resource方法,为资源设置路由。
实例化flask-restful组件的Api类之后,调用其方法add_resource添加资源,并在对应资源中实现增删改查等HTTP方法。
示例:
api.add_resource(HelloWorld, '/')
通过把URL的一部分标记为<variable_name>就可以在URL中添加变量。标记的部分会作为关键字参数传递给函数。
通过使用<converter:variable_name>,可以选择性的加上一个转换器,为变量指定规则。
示例:
- @app.route('/user/<username>')
- def show_user_profile(username):
- return 'User %s' % escape(username)
-
- @app.route('/post/<int:post_id>')
- def show_post(post_id):
- # show the post with the given id, the id is an integer
- return 'Post %d' % post_id
1)flask_restful实现请求url数据获取及校验。
在api.add_resource()的第二个参数路径中加上URL参数变量即可,也支持转换器来转换变量类型。此外,在User类的get(),post(),put()等成员函数中,添加参数user_id直接获取传入的变量值。
示例:
- class User(Resource):
- def get(self, user_id):
- return USER_LIST[user_id]
- api.add_resource(User, '/users/<user_id>')
2)flask_restful实现请求参数校验与获取
Flask-RESTful提供了请求解析工具 reqparse,可以获取客户端的请求数据,还可以对数据进行校验并返回合适的错误消息,简化参数处理。类似命令行解析工具argparse。
reqparse工具用于对Flask中flask.request对象上的任何变量,提供简单和统一的访问方式。
可以调用reqparse.RequestParser()类实例化对象的add_argument方法,对请求参数进行校验。可以指定具体参数的类型、指定参数是否为必选项、自定义错误消息等等,对请求参数进行约束校验,并做异常处理。
校验成功之后,就可以在post和put方法中,调用parser.parse_args()获取表单参数,返回的是一个字典。
add_argument(*args, **kwargs),用于向RequestParser类的实例化对象中,添加要解析的参数。
add_argument对应的Argument构造函数,如下:
- class reqparse.Argument(name, default=None, dest=None, required=False, ignore=False,
- type=<function <lambda>>, location=('json', 'values'), choices=(), action='store',
- help=None, operators=('=', ), case_sensitive=True, store_missing=True, trim=False, nullable=True)
参数说明:
required,表示参数是否可以省略。True表示指定name的参数为必选参数,False表示为可缺省参数。
help,若指定help参数,可用于自定义类型错误返回信息。否则,默认是返回类型错误本身的信息。
示例:
- from flask_restful import reqparse
- parser = reqparse.RequestParser()
- parser.add_argument('rate', type=int, help='Rate cannot be converted') # 向parser对象中添加要关注的命令行参数和选项
- parser.add_argument('name', type=str) # python2默认参数类型是unicode字符串。
- args = parser.parse_args() # 调用parse_args()方法获取解析器中的参数并校验。
- class User(Resource):
- def get(self, user_id):
- return USER_LIST[user_id]
- api.add_resource(User, '/users/<user_id>')
参考:
http://www.pythondoc.com/Flask-RESTful/reqparse.html
https://flask-restful.readthedocs.io/en/latest/api.html#reqparse.Argument
Web应用使用不同的HTTP方法处理 URL。缺省情况下,一个路由只回应GET请求。
首先,需要flask模块导入请求对象request。
然后,使用route()装饰器的methods参数,来处理不同的HTTP方法。如下:
- from flask import request
- @app.route('/login', methods=['POST', 'GET'])
- def login():
- if request.method == 'POST':
- if valid_login(request.form['username'],request.form['password']):
- return log_the_user_in(request.form['username'])
- else:
- error = 'Invalid username/password'
- return render_template('login.html', error=error)
说明:
(1)通过读取request的method属性,可以获取当前请求方法
(2)通过使用request的form属性,来处理请求中的表单数据(在POST或者PUT请求中传输的数据)。
(3)request url中获取参数,操作URL(如?key=value)中提交的参数可以使用request的args属性,例如: searchword = request.args.get('key', '')
Flask-RESTful提供的最主要的基础就是资源(resources)。
资源(Resources)是构建在Flask视图之上,只要在资源(resource)上定义对应的CRUD方法,就能够容易地访问多个 HTTP 方法。
示例如下:
- class TodoSimple(Resource):
- def get(self, todo_id):
- return {todo_id: todos[todo_id]}
- def put(self, todo_id):
- todos[todo_id] = request.form['data']
- return {todo_id: todos[todo_id]}
- api.add_resource(TodoSimple, '/<string:todo_id>')
使用flask-restful的Api.add_resource方法添加路由,实例化flask-restful组件的Api类之后,调用其方法add_resource添加资源,并在对应资源中实现增删改查等HTTP方法。add_resource方法格式:
add_resource(resource, *urls, **kwargs)
参数描述:
resource (Type[Resource]),自定义resource的类名,该类继承Resource基类,其成员函数定义了不同的HTTP请求方法的逻辑
urls (str),此参数定义了URL路径,可以匹配一个或多个url路由。
用法示例:
- api.add_resource(HelloWorld, '/', '/hello') # 访问http://localhost:5000/和http://localhost:5000/hello效果完全一样。
-
- api.add_resource(Foo, '/foo', endpoint="foo")
-
- api.add_resource(FooSpecial, '/special/foo', endpoint="foo")
应用举例:
- from flask import Flask
- from flask_restful import Resource, Api
- app = Flask(__name__)
- api = Api(app)
- class HelloWorld(Resource):
- def get(self):
- return {'hello': 'world'}
- api.add_resource(HelloWorld, '/')
https://flask-restful.readthedocs.io/en/latest/api.html
add_resource的作用:通过url,查询对应的函数名
url_for的作用:通过函数名,反向查询对应URL地址
add_resource 函数使用指定的endpoint注册路由到框架上。如果没有指定endpoint,Flask-RESTful会根据类名生成一个,但是有时候有些函数比如 url_for需要endpoint,因此建议给endpoint 赋值。
示例:
api.add_resource(FooSpecial, '/special/foo', endpoint="foo")
在add_resource方法中,设置路由的 endpoint ,若未指定,则endpoint为资源类名的小写形式。
endpoint 是Flask中对具体路由的内部的具体定义,一般作为 url_for 方法的第一个参数,即通过 endpoint 获得该路由的URL,在列出RESTful资源URL时非常有用。
如果知道了视图函数,要怎么找到url呢?这时候就需要url_for函数。
(1)反向构建通常比硬编码的描述性更好。将来如果修改了URL, 但没有修改该URL对应的函数名,就不用到处支替换URL了.
(2)url_for会自动的处理特殊的字符,无须手动处理
传入函数名,得到函数的路由地址(访问视图函数的地址)。url_for就是高级版的重定向redirect,类似yield是高级版的return。
url_for()函数用于构建指定函数的URL。它把函数名称作为第一个参数,可以接受任意个关键字参数,每个关键字参数对应URL中的变量。未知变量部分会添加到 URL 末尾作为查询参数。
- @app.route('/')
- def index():
- pass
- @app.route('/login')
- def login():
- pass
- @app.route('/user/<username>')
- def profile(username):
- pass
- print url_for('index') # 输出 /
- print url_for('login') # 输出 /login
- print url_for('login', next='/') # 输出 /login?next=/
- print url_for('profile', username='John Doe') # 输出 /user/John%20Doe
flask-Restful标准化返回参数,Flask-RESTful 提供了 fields 模块和 marshal_with() 装饰器。
导入flask_restful.marshal_with装饰器,需要写一个字典,来指示要返回的字段名,以及该字段的数据类型。
marshal方法及参数描述:
flask_restful.marshal(data, fields, envelope=None)
data:字段所在的实际对象
fields:一个字典,其键将构成最终的序列化响应输出
marshal_with方法描述:
flask_restful.marshal_with(fields, envelope=None)
示例:
- >>> from flask_restful import fields, marshal_with
- >>> mfields = { 'a': fields.Raw }
- >>> @marshal_with(mfields)
- ... def get():
- ... return { 'a': 100, 'b': 'foo' }
- >>> get()
- OrderedDict([('a', 100)])
响应:即来自服务器的应答。 HTTP遵循经典的客户端-服务端模型,客户端打开一个连接以发出请求,然后等待它收到服务器端响应。
HTTP响应报文由三部分组成:响应状态码(Response Status Code)、响应头(Response Headers)、响应体(Response Body)
app在视图函数处理逻辑完成后,得到响应需要的Body, code和header,然后调用make_response方法创建一个Response对象。
Response对象负责接收经过视图函数处理后的返回数据,将其解析编码成http协议要求的数据格式,然后调用http服务器的回调函数同时将数据返回给客户端。
每一个请求都会有一个Response对象。
HTTP状态码说明参考:
https://www.ietf.org/rfc/rfc2068.txt
https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81
Flask框架允许将默认的响应类,替换为自定义类。
在网络应用中,路由通常最后会调用render_template函数,渲染引用的模板文件,将其作为字符串返回,此时还可以指定HTTP状态码和自定义的HTTP响应标头,如下:
return render_template('data.json'), 201, {'Content-Type': 'application/json'}
视图函数的返回值会被自动转换为一个响应对象。如果返回值是一个字符串,它被转换为该字符串为状态码为200、MIME类型为text/html的响应对象。
Flask 把返回值转换为响应对象的逻辑是这样:
(1)如果返回的是一个合法的响应对象,它会从视图直接返回。
(2)如果返回的是一个字符串,响应对象会用字符串数据和默认参数创建。
(3)如果返回的是一个元组,且元组中的元素可以提供额外的信息。这样的元组必须是(response, status, headers)的形式,且至少包含一个元素。
status值会覆盖状态代码,headers可以是一个列表或字典,作为额外的消息标头值。
(4)如果上述条件均不满足,Flask会假设返回值是一个合法的WSGI应用程序,并转换为一个请求对象。
如果想在视图里操纵上述步骤结果的响应对象,可以使用 make_response() 函数。只需要把返回值表达式传递给 make_response() ,获取结果对象并修改,然后再返回它:
- @app.errorhandler(404)
- def not_found(error):
- resp = make_response(render_template('error.html'), 404)
- resp.headers['X-Something'] = 'A value'
- return resp
复杂的项目需要各种配置参数。
如果配置项较少,可以直接硬编码,将变量直接写在源码中,此方法简单,但不灵活,且可能有安全问题(源码中不存账号密码),生产环境不推荐使用。
如果设置选项很多,想要集中管理设置项,应该将他们存放到一个文件里面。
- app =Flask(__name__)
-
- app.config['DEBUG']=True
此处配置模块文件必须是python格式,且导入from_object的参数只写模块名,不带.py后缀
- # cat yourconfig.py
-
- USERID = 'wuyongpeng'
-
- SECERET = 12345678
-
- IS_ROOT = True
配置文件导入,及变量读取方法:
- app.config.from_object('yourconfig')
-
- (Pdb) app.config["USERID"]
-
- 'wuyongpeng'
配置文件导入方法:
app.config.from_pyfile('yourconfig.cfg')
注意,此处自定义配置文件yourconfig.cfg的配置项对应key值应该全为大写,value值可以为字符、数字、bool类型。如下:
- # cat yourconfig.cfg
-
- USERID = 'wuyongpeng'
-
- SECERET = 12345678
-
- IS_ROOT = True
from_object()方法可以接受一个模块作为参数,在Python中,一个py文件就是一个模块,例如abc.py模块的模块名是abc;
from_pyfile()方法接受的就是一个py文件的文件名作为参数,例如abc.py文件的文件名是abc.py。
这种情况下,在启动应用程序之前,必须将这个环境变量设置为想要使用的文件。例如:
export YOURAPPLICATION_SETTINGS='/path/to/config/file'
读取变量方法:
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
- (Pdb) app.config
- <Config {'JSON_AS_ASCII': True, 'USE_X_SENDFILE': False, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_NAME': 'session', 'LOGGER_NAME': '__main__', 'DEBUG': False, 'SECRET_KEY': None, 'MAX_CONTENT_LENGTH': None, 'APPLICATION_ROOT': None, 'SERVER_NAME': None, 'PREFERRED_URL_SCHEME': 'http', 'JSONIFY_PRETTYPRINT_REGULAR': True, 'TESTING': False, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'PROPAGATE_EXCEPTIONS': None, 'TRAP_BAD_REQUEST_ERRORS': False, 'JSON_SORT_KEYS': True, 'SESSION_COOKIE_HTTPONLY': True, 'SEND_FILE_MAX_AGE_DEFAULT': 43200, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SESSION_COOKIE_SECURE': False, 'TRAP_HTTP_EXCEPTIONS': False}>
- (Pdb) type(app.config)
- <class 'flask.config.Config'>
- (Pdb)
from_json(filename, silent=False)
filename—JSON文件的文件名。这可以是一个绝对文件名,也可以是相对于根路径的文件名。
silent -如果你想让丢失的文件静默失败,则设置为True。
configparser模块用于在Python中读取配置文件。
配置文件的格式如下: 中括号“[ ]”内包含的为section,section 下面为key-value 格式的配置内容
- [DEFAULT]
-
- ServerAliveInterval = 45
-
- Compression = yes
-
- CompressionLevel = 9
-
- [topsecret.server.com]
-
- Port = 50022
-
- ForwardX11 = no
configparser模块常用方法:
RawConfigParser.sections() # 列出除DEFAULT之外的所有section列表
RawConfigParser.add_section(section) # 添加section
RawConfigParser.has_section(section) # 判断指定section是否存在
RawConfigParser.options(section) # 返回指定section的所有值
RawConfigParser.has_option(section, option) # 判断指定section组中是否存在指定option
RawConfigParser.read(filenames) # 读取配置文件
RawConfigParser.get(section, option) # 获取指定section组中指定option的值
RawConfigParser.set(section, option, value) # 给指定section的指定option设置指定的值
应用举例:
- import ConfigParser
-
- configpath = "/etc/galaxycloud_monitor/monitor.conf"
-
- cf = ConfigParser.SafeConfigParser()
-
- cf.read(configpath)
-
- users_info_url = cf.get("DEFAULT","users_info_url")
https://docs.python.org/2.7/library/configparser.html
https://dormousehole.readthedocs.io/en/latest/config.html
https://dormousehole.readthedocs.io/en/latest/api.html#flask.Config
有时候可能会遇到数据出错需要纠正的情况。例如因为用户篡改了数据或客户端代码出错而导致一个客户端代码向服务器发送了明显错误的HTTP请求。多数时候在类似情况下返回400 Bad Request 就没事了,但也有不会返回的时候,而代码还得继续运行下去。
这时候就需要使用日志来记录这些不正常的东西了。
在Flask项目中,可以使用Flask基于logging模块封装的app.logger记录日志,也可以使用Python的logging模块记录日志。
Flask使用标准的Python日志记录。所有与Flask相关的消息都记录在“Flask”日志命名空间下
logger是一个标准的Logger Logger类,日志调用示例:
- app.logger.debug('A value for debugging')
- app.logger.info('%s logged in successfully', user.username)
- app.logger.warning('A warning occurred (%d apples)', 42)
- app.logger.error('An error occurred')
调用示例:
- import logging
- log_file_path = "/var/log/monitor.log"
- logging.basicConfig(filename=log_file_path,
- format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
- datefmt='%Y-%m-%d %H:%M:%S')
扩展:python logging模块分析 https://docs.python.org/3/library/logging.html
abort方法可以传递一个WSGI应用程序或一个状态码。
如果给出了一个状态代码,它会在异常列表中查找并引发该异常;如果传递给一个WSGI应用程序,它会把它包装在一个代理WSGI异常中并引发。
使用abort()可以更早退出请求,并返回错误代码,调用结构如下:
flask.abort(status, *args, **kwargs)
使用示例:
- abort(404)
- abort(400, message="user id {} not exist!".format(vm_user_id))
- abort(Response('Hello World'))
errorhandler方法用于异常捕获,可以自定义错误处理方法。结构如下:
errorhandler(code_or_exception)
示例:
- @app.errorhandler(404)
- def page_not_found(error):
- """自定义的处理错误方法"""
- """返回值可让用户看到具体异常信息"""
- return 'This page does not exist', 404
当一个异常发生时,如果没有对应的异常处理器,那么就会返回一个 500 内部服务错误。即调用flask.Flask.handle_exception()。
参考:
https://dormousehole.readthedocs.io/en/latest/errorhandling.html
https://dormousehole.readthedocs.io/en/latest/quickstart.html#quickstart
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。